1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* Tools to use API as ActiveRecord for Yii2 |
5
|
|
|
* |
6
|
|
|
* @link https://github.com/hiqdev/yii2-hiart |
7
|
|
|
* @package yii2-hiart |
8
|
|
|
* @license BSD-3-Clause |
9
|
|
|
* @copyright Copyright (c) 2015-2016, HiQDev (http://hiqdev.com/) |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace hiqdev\hiart; |
13
|
|
|
|
14
|
|
|
use yii\base\InvalidParamException; |
15
|
|
|
use yii\base\NotSupportedException; |
16
|
|
|
use yii\helpers\ArrayHelper; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* QueryBuilder builds an HiArt query based on the specification given as a [[Query]] object. |
20
|
|
|
*/ |
21
|
|
|
class QueryBuilder extends \yii\base\Object |
22
|
|
|
{ |
23
|
|
|
private $_sort = [ |
24
|
|
|
SORT_ASC => '_asc', |
25
|
|
|
SORT_DESC => '_desc', |
26
|
|
|
]; |
27
|
|
|
|
28
|
|
|
public $db; |
29
|
|
|
|
30
|
|
|
public function __construct($connection, $config = []) |
31
|
|
|
{ |
32
|
|
|
$this->db = $connection; |
33
|
|
|
parent::__construct($config); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* @param ActiveQuery $query |
38
|
|
|
* |
39
|
|
|
* @throws NotSupportedException |
40
|
|
|
* |
41
|
|
|
* @return array |
42
|
|
|
*/ |
43
|
|
|
public function build($query) |
44
|
|
|
{ |
45
|
|
|
$parts = []; |
46
|
|
|
$query->prepare(); |
47
|
|
|
|
48
|
|
|
$this->buildSelect($query->select, $parts); |
49
|
|
|
$this->buildLimit($query->limit, $parts); |
50
|
|
|
$this->buildPage($query->offset, $query->limit, $parts); |
51
|
|
|
$this->buildOrderBy($query->orderBy, $parts); |
52
|
|
|
|
53
|
|
|
$parts = ArrayHelper::merge($parts, $this->buildCondition($query->where)); |
54
|
|
|
|
55
|
|
|
return [ |
56
|
|
|
'queryParts' => $parts, |
57
|
|
|
'index' => $query->index, |
58
|
|
|
'type' => $query->type, |
59
|
|
|
]; |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
public function buildLimit($limit, &$parts) |
63
|
|
|
{ |
64
|
|
|
if (!empty($limit)) { |
65
|
|
|
if ($limit === -1) { |
66
|
|
|
$limit = 'ALL'; |
67
|
|
|
} |
68
|
|
|
$parts['limit'] = $limit; |
69
|
|
|
} |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
public function buildPage($offset, $limit, &$parts) |
73
|
|
|
{ |
74
|
|
|
if ($offset > 0) { |
75
|
|
|
$parts['page'] = ceil($offset / $limit) + 1; |
76
|
|
|
} |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
public function buildOrderBy($orderBy, &$parts) |
80
|
|
|
{ |
81
|
|
|
if (!empty($orderBy)) { |
82
|
|
|
$parts['orderby'] = key($orderBy) . $this->_sort[reset($orderBy)]; |
83
|
|
|
} |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
public function buildSelect($select, &$parts) |
87
|
|
|
{ |
88
|
|
|
if (!empty($select)) { |
89
|
|
|
foreach ($select as $attribute) { |
90
|
|
|
$parts['select'][$attribute] = $attribute; |
91
|
|
|
} |
92
|
|
|
} |
93
|
|
|
} |
94
|
|
|
|
95
|
|
|
public function buildCondition($condition) |
96
|
|
|
{ |
97
|
|
|
static $builders = [ |
98
|
|
|
'and' => 'buildAndCondition', |
99
|
|
|
'between' => 'buildBetweenCondition', |
100
|
|
|
'eq' => 'buildEqCondition', |
101
|
|
|
'in' => 'buildInCondition', |
102
|
|
|
'like' => 'buildLikeCondition', |
103
|
|
|
'gt' => 'buildCompareCondition', |
104
|
|
|
'ge' => 'buildCompareCondition', |
105
|
|
|
'lt' => 'buildCompareCondition', |
106
|
|
|
'le' => 'buildCompareCondition', |
107
|
|
|
]; |
108
|
|
|
if (empty($condition)) { |
109
|
|
|
return []; |
110
|
|
|
} |
111
|
|
|
if (!is_array($condition)) { |
112
|
|
|
throw new NotSupportedException('String conditions in where() are not supported by HiArt.'); |
113
|
|
|
} |
114
|
|
|
|
115
|
|
|
if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ... |
116
|
|
|
$operator = strtolower($condition[0]); |
117
|
|
|
if (isset($builders[$operator])) { |
118
|
|
|
$method = $builders[$operator]; |
119
|
|
|
array_shift($condition); // Shift build condition |
120
|
|
|
|
121
|
|
|
return $this->$method($operator, $condition); |
122
|
|
|
} else { |
123
|
|
|
throw new InvalidParamException('Found unknown operator in query: ' . $operator); |
124
|
|
|
} |
125
|
|
|
} else { |
126
|
|
|
return $this->buildHashCondition($condition); |
127
|
|
|
} |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
private function buildHashCondition($condition) |
131
|
|
|
{ |
132
|
|
|
$parts = []; |
133
|
|
|
foreach ($condition as $attribute => $value) { |
134
|
|
|
if (is_array($value)) { // IN condition |
135
|
|
|
// $parts[] = [$attribute.'s' => join(',',$value)]; |
|
|
|
|
136
|
|
|
$parts[$attribute . 's'] = implode(',', $value); |
137
|
|
|
} else { |
138
|
|
|
$parts[$attribute] = $value; |
139
|
|
|
} |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
return $parts; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
private function buildLikeCondition($operator, $operands) |
|
|
|
|
146
|
|
|
{ |
147
|
|
|
return [$operands[0] . '_like' => $operands[1]]; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
private function buildCompareCondition($operator, $operands) |
151
|
|
|
{ |
152
|
|
|
if (!isset($operands[0], $operands[1])) { |
153
|
|
|
throw new InvalidParamException("Operator '$operator' requires three operands."); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
return [$operands[0] . '_' . $operator => $operands[1]]; |
157
|
|
|
} |
158
|
|
|
|
159
|
|
|
private function buildAndCondition($operator, $operands) |
|
|
|
|
160
|
|
|
{ |
161
|
|
|
$parts = []; |
162
|
|
|
foreach ($operands as $operand) { |
163
|
|
|
if (is_array($operand)) { |
164
|
|
|
$parts = \yii\helpers\ArrayHelper::merge($this->buildCondition($operand), $parts); |
165
|
|
|
} |
166
|
|
|
} |
167
|
|
|
if (!empty($parts)) { |
168
|
|
|
return $parts; |
169
|
|
|
} else { |
170
|
|
|
return []; |
171
|
|
|
} |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
private function buildBetweenCondition($operator, $operands) |
|
|
|
|
175
|
|
|
{ |
176
|
|
|
throw new NotSupportedException('Between condition is not supported by HiArt.'); |
177
|
|
|
} |
178
|
|
|
|
179
|
|
|
private function buildInCondition($operator, $operands) |
180
|
|
|
{ |
181
|
|
|
if (!isset($operands[0], $operands[1])) { |
182
|
|
|
throw new InvalidParamException("Operator '$operator' requires two operands."); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
list($column, $values) = $operands; |
186
|
|
|
|
187
|
|
|
if (count($column) > 1) { |
188
|
|
|
return $this->buildCompositeInCondition($operator, $column, $values); |
189
|
|
|
} elseif (is_array($column)) { |
190
|
|
|
$column = reset($column); |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
foreach ((array) $values as $i => $value) { |
194
|
|
|
if (is_array($value)) { |
195
|
|
|
$values[$i] = $value = isset($value[$column]) ? $value[$column] : null; |
196
|
|
|
} |
197
|
|
|
if ($value === null) { |
198
|
|
|
unset($values[$i]); |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
return [$column . '_in' => $values]; |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
private function buildEqCondition($operator, $operands) |
|
|
|
|
206
|
|
|
{ |
207
|
|
|
$key = array_shift($operands); |
208
|
|
|
|
209
|
|
|
return [$key => reset($operands)]; |
210
|
|
|
} |
211
|
|
|
|
212
|
|
|
protected function buildCompositeInCondition($operator, $columns, $values) |
|
|
|
|
213
|
|
|
{ |
214
|
|
|
throw new NotSupportedException('composite in is not supported by HiArt.'); |
215
|
|
|
} |
216
|
|
|
} |
217
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.