1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* @link https://github.com/chrmorandi/yii2-ldap for the canonical source repository |
4
|
|
|
* @package yii2-ldap |
5
|
|
|
* @author Christopher Mota <[email protected]> |
6
|
|
|
* @license Mit License - view the LICENSE file that was distributed with this source code. |
7
|
|
|
*/ |
8
|
|
|
|
9
|
|
|
namespace chrmorandi\ldap; |
10
|
|
|
|
11
|
|
|
use Yii; |
12
|
|
|
use yii\base\Component; |
13
|
|
|
use chrmorandi\ldap\Connection; |
14
|
|
|
use yii\db\BatchQueryResult; |
15
|
|
|
use yii\db\Expression; |
16
|
|
|
use yii\db\QueryInterface; |
17
|
|
|
use yii\db\QueryTrait; |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* Query represents a SEARCH in LDAP database directory. |
21
|
|
|
* |
22
|
|
|
* Query provides a set of methods to facilitate the specification of different clauses |
23
|
|
|
* in a SEARCH statement. These methods can be chained together. |
24
|
|
|
* |
25
|
|
|
* By calling [[createFilter()]], we can get a [[Command]] instance which can be further |
26
|
|
|
* used to perform/execute the LDAP query against a database directory. |
27
|
|
|
* |
28
|
|
|
* For example, |
29
|
|
|
* |
30
|
|
|
* ```php |
31
|
|
|
* $query = new Query; |
32
|
|
|
* // compose the query |
33
|
|
|
* $query->select('id, name') |
34
|
|
|
* ->from('user') |
35
|
|
|
* ->limit(10); |
36
|
|
|
* // build and execute the query |
37
|
|
|
* $rows = $query->all(); |
38
|
|
|
* // alternatively, you can create LDAP command and execute it |
39
|
|
|
* $command = $query->createFilter(); |
40
|
|
|
* // $command->argument returns the actual LDAP arguments for the search |
41
|
|
|
* $rows = $command->queryAll(); |
42
|
|
|
* ``` |
43
|
|
|
* |
44
|
|
|
* Query internally uses the [[QueryBuilder]] class to generate the LDAP statement. |
45
|
|
|
* |
46
|
|
|
* @author Christopher Mota <[email protected]> |
47
|
|
|
* @since 1.0.0 |
48
|
|
|
*/ |
49
|
|
|
class Query extends Component implements QueryInterface |
50
|
|
|
{ |
51
|
|
|
use QueryTrait; |
52
|
|
|
|
53
|
|
|
const SEARCH_SCOPE_SUB = 'ldap_search'; |
54
|
|
|
const SEARCH_SCOPE_ONE = 'ldap_list'; |
55
|
|
|
const SEARCH_SCOPE_BASE = 'ldap_read'; |
56
|
|
|
|
57
|
|
|
/** |
58
|
|
|
* @var string the scope of search |
59
|
|
|
* The search scope: |
60
|
|
|
* Query::SEARCH_SCOPE_SUB searches the complete subtree including the $baseDn node. This is the default value. |
61
|
|
|
* Query::SEARCH_SCOPE_ONE restricts search to one level below $baseDn. |
62
|
|
|
* Query::SEARCH_SCOPE_BASE restricts search to the $baseDn itself; this can be used to efficiently retrieve a single entry by its DN. |
63
|
|
|
*/ |
64
|
|
|
public $scope = self::SEARCH_SCOPE_SUB; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var array the columns being selected. For example, `['id', 'name']`. |
68
|
|
|
* This is used to construct the SEARCH function in a LDAP statement. If not set, it means selecting all columns. |
69
|
|
|
* @see select() |
70
|
|
|
*/ |
71
|
|
|
public $select; |
72
|
|
|
|
73
|
|
|
/** |
74
|
|
|
* @var boolean whether to select distinct rows of data only. If this is set true, |
75
|
|
|
* the return of SEARCH funtion not show duplicade results. |
76
|
|
|
*/ |
77
|
|
|
public $distinct; |
78
|
|
|
|
79
|
|
|
/** |
80
|
|
|
* @var array the table(s) to be selected from. For example, `['user', 'post']`. |
81
|
|
|
* This is used to construct the FROM clause in a SQL statement. |
82
|
|
|
* @see from() |
83
|
|
|
*/ |
84
|
|
|
public $from; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @var array how to group the query results. For example, `['company', 'department']`. |
88
|
|
|
* This is used to construct the GROUP BY clause in a SQL statement. |
89
|
|
|
*/ |
90
|
|
|
public $groupBy; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* @var string|array the condition to be applied in the GROUP BY clause. |
94
|
|
|
* It can be either a string or an array. Please refer to [[where()]] on how to specify the condition. |
95
|
|
|
*/ |
96
|
|
|
public $having; |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* Creates a LDAP filter that can be used to execute this query. |
100
|
|
|
* @param Connection $db the database connection used to generate the SQL statement. |
101
|
|
|
* If this parameter is not given, the `db` application component will be used. |
102
|
|
|
* @return Array with string base DN, string filter, An array of the required attributes and bool. |
103
|
|
|
*/ |
104
|
|
|
public function createFilter($db = null) |
105
|
|
|
{ |
106
|
|
|
if ($db === null) { |
107
|
|
|
$db = Yii::$app->get('ldap'); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
$filter = (new FilterBuilder)->build($this->where); |
111
|
|
|
$select = (is_array($this->select))?$this->select:[]; |
112
|
|
|
|
113
|
|
|
$params = [ |
114
|
|
|
$db->baseDn, |
115
|
|
|
$filter, |
116
|
|
|
$select, |
117
|
|
|
0 |
118
|
|
|
]; |
119
|
|
|
|
120
|
|
|
return $params; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Starts a batch query. |
125
|
|
|
* |
126
|
|
|
* A batch query supports fetching data in batches, which can keep the memory usage under a limit. |
127
|
|
|
* This method will return a [[BatchQueryResult]] object which implements the [[\Iterator]] interface |
128
|
|
|
* and can be traversed to retrieve the data in batches. |
129
|
|
|
* |
130
|
|
|
* For example, |
131
|
|
|
* |
132
|
|
|
* ```php |
133
|
|
|
* $query = (new Query)->from('user'); |
134
|
|
|
* foreach ($query->batch() as $rows) { |
135
|
|
|
* // $rows is an array of 10 or fewer rows from user table |
136
|
|
|
* } |
137
|
|
|
* ``` |
138
|
|
|
* |
139
|
|
|
* @param integer $batchSize the number of records to be fetched in each batch. |
140
|
|
|
* @param Connection $db the database connection. If not set, the "db" application component will be used. |
141
|
|
|
* @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface |
142
|
|
|
* and can be traversed to retrieve the data in batches. |
143
|
|
|
*/ |
144
|
|
|
// public function batch($batchSize = 100, $db = null) |
|
|
|
|
145
|
|
|
// { |
146
|
|
|
// return Yii::createObject([ |
147
|
|
|
// 'class' => BatchQueryResult::className(), |
148
|
|
|
// 'query' => $this, |
149
|
|
|
// 'batchSize' => $batchSize, |
150
|
|
|
// 'db' => $db, |
151
|
|
|
// 'each' => false, |
152
|
|
|
// ]); |
153
|
|
|
// } |
154
|
|
|
|
155
|
|
|
/** |
156
|
|
|
* Starts a batch query and retrieves data row by row. |
157
|
|
|
* This method is similar to [[batch()]] except that in each iteration of the result, |
158
|
|
|
* only one row of data is returned. For example, |
159
|
|
|
* |
160
|
|
|
* ```php |
161
|
|
|
* $query = (new Query)->from('user'); |
162
|
|
|
* foreach ($query->each() as $row) { |
163
|
|
|
* } |
164
|
|
|
* ``` |
165
|
|
|
* |
166
|
|
|
* @param integer $batchSize the number of records to be fetched in each batch. |
167
|
|
|
* @param Connection $db the database connection. If not set, the "db" application component will be used. |
168
|
|
|
* @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface |
169
|
|
|
* and can be traversed to retrieve the data in batches. |
170
|
|
|
*/ |
171
|
|
|
// public function each($batchSize = 100, $db = null) |
|
|
|
|
172
|
|
|
// { |
173
|
|
|
// return Yii::createObject([ |
174
|
|
|
// 'class' => BatchQueryResult::className(), |
175
|
|
|
// 'query' => $this, |
176
|
|
|
// 'batchSize' => $batchSize, |
177
|
|
|
// 'db' => $db, |
178
|
|
|
// 'each' => true, |
179
|
|
|
// ]); |
180
|
|
|
// } |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Executes the query and returns all results as an array. |
184
|
|
|
* @param Connection $db the database connection used to generate the SQL statement. |
185
|
|
|
* If this parameter is not given, the `db` application component will be used. |
186
|
|
|
* @return array the query results. If the query results in nothing, an empty array will be returned. |
187
|
|
|
*/ |
188
|
|
|
public function all($db = null) |
189
|
|
|
{ |
190
|
|
|
if ($db === null) { |
191
|
|
|
$db = Yii::$app->get('ldap'); |
192
|
|
|
} |
193
|
|
|
|
194
|
|
|
$params = $this->createFilter($db); |
195
|
|
|
|
196
|
|
|
/** @var $result DataReader */ |
197
|
|
|
$result = $db->execute($this->scope, $params); |
198
|
|
|
return $this->populate($result->toArray()); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Converts the raw query results into the format as specified by this query. |
203
|
|
|
* This method is internally used to convert the data fetched from database |
204
|
|
|
* into the format as required by this query. |
205
|
|
|
* @param array $rows the raw query result from database |
206
|
|
|
* @return array the converted query result |
207
|
|
|
*/ |
208
|
|
|
public function populate($rows) |
209
|
|
|
{ |
210
|
|
|
if ($this->indexBy === null) { |
211
|
|
|
return $rows; |
212
|
|
|
} |
213
|
|
|
$result = []; |
214
|
|
|
foreach ($rows as $row) { |
215
|
|
|
if (is_string($this->indexBy)) { |
216
|
|
|
$key = $row[$this->indexBy]; |
217
|
|
|
} else { |
218
|
|
|
$key = call_user_func($this->indexBy, $row); |
219
|
|
|
} |
220
|
|
|
$result[$key] = $row; |
221
|
|
|
} |
222
|
|
|
return $result; |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* Executes the query and returns a single row of result. |
227
|
|
|
* @param Connection $db the database connection used to generate the SQL statement. |
228
|
|
|
* If this parameter is not given, the `db` application component will be used. |
229
|
|
|
* @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query |
230
|
|
|
* results in nothing. |
231
|
|
|
*/ |
232
|
|
|
public function one($db = null) |
233
|
|
|
{ |
234
|
|
|
$params = $this->createFilter($db); |
235
|
|
|
|
236
|
|
|
/** @var $result DataReader */ |
237
|
|
|
$result = $db->execute($this->scope, array_push($params,1)); |
|
|
|
|
238
|
|
|
return $result->toArray(); |
239
|
|
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* Returns the query result as a scalar value. |
243
|
|
|
* The value returned will be the first column in the first row of the query results. |
244
|
|
|
* @param Connection $db the database connection used to generate the SQL statement. |
245
|
|
|
* If this parameter is not given, the `db` application component will be used. |
246
|
|
|
* @return string|boolean the value of the first column in the first row of the query result. |
247
|
|
|
* False is returned if the query result is empty. |
248
|
|
|
*/ |
249
|
|
|
// public function scalar($db = null) |
|
|
|
|
250
|
|
|
// { |
251
|
|
|
// return $this->createFilter($db)->queryScalar(); |
252
|
|
|
// } |
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* Executes the query and returns the first column of the result. |
256
|
|
|
* @param Connection $db the database connection used to generate the SQL statement. |
257
|
|
|
* If this parameter is not given, the `db` application component will be used. |
258
|
|
|
* @return array the first column of the query result. An empty array is returned if the query results in nothing. |
259
|
|
|
*/ |
260
|
|
|
// public function column($db = null) |
|
|
|
|
261
|
|
|
// { |
262
|
|
|
// if (!is_string($this->indexBy)) { |
263
|
|
|
// return $this->createFilter($db)->queryColumn(); |
264
|
|
|
// } |
265
|
|
|
// if (is_array($this->select) && count($this->select) === 1) { |
266
|
|
|
// $this->select[] = $this->indexBy; |
267
|
|
|
// } |
268
|
|
|
// $rows = $this->createFilter($db)->queryAll(); |
269
|
|
|
// $results = []; |
270
|
|
|
// foreach ($rows as $row) { |
271
|
|
|
// if (array_key_exists($this->indexBy, $row)) { |
272
|
|
|
// $results[$row[$this->indexBy]] = reset($row); |
273
|
|
|
// } else { |
274
|
|
|
// $results[] = reset($row); |
275
|
|
|
// } |
276
|
|
|
// } |
277
|
|
|
// return $results; |
278
|
|
|
// } |
279
|
|
|
|
280
|
|
|
/** |
281
|
|
|
* Returns the number of records. |
282
|
|
|
* @param string $q the COUNT expression. Defaults to '*'. |
283
|
|
|
* Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression. |
284
|
|
|
* @param Connection $db the database connection used to generate the SQL statement. |
285
|
|
|
* If this parameter is not given (or null), the `db` application component will be used. |
286
|
|
|
* @return integer|string number of records. The result may be a string depending on the |
287
|
|
|
* underlying database engine and to support integer values higher than a 32bit PHP integer can handle. |
288
|
|
|
*/ |
289
|
|
|
public function count($q = '*', $db = null) |
290
|
|
|
{ |
291
|
|
|
return $this->queryScalar("COUNT($q)", $db); |
|
|
|
|
292
|
|
|
} |
293
|
|
|
|
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Returns a value indicating whether the query result contains any row of data. |
297
|
|
|
* @param Connection $db the database connection used to generate the SQL statement. |
298
|
|
|
* If this parameter is not given, the `db` application component will be used. |
299
|
|
|
* @return boolean whether the query result contains any row of data. |
300
|
|
|
*/ |
301
|
|
|
public function exists($db = null) |
302
|
|
|
{ |
303
|
|
|
$command = $this->createFilter($db); |
304
|
|
|
$params = $command->params; |
305
|
|
|
$command->setSql($command->db->getQueryBuilder()->selectExists($command->getSql())); |
|
|
|
|
306
|
|
|
$command->bindValues($params); |
|
|
|
|
307
|
|
|
return (boolean)$command->queryScalar(); |
|
|
|
|
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Queries a scalar value by setting [[select]] first. |
312
|
|
|
* Restores the value of select to make this query reusable. |
313
|
|
|
* @param string|Expression $selectExpression |
314
|
|
|
* @param Connection|null $db |
315
|
|
|
* @return boolean|string |
316
|
|
|
*/ |
317
|
|
|
// protected function queryScalar($selectExpression, $db) |
|
|
|
|
318
|
|
|
// { |
319
|
|
|
// $select = $this->select; |
320
|
|
|
// $limit = $this->limit; |
321
|
|
|
// $offset = $this->offset; |
322
|
|
|
// |
323
|
|
|
// $this->select = [$selectExpression]; |
324
|
|
|
// $this->limit = null; |
325
|
|
|
// $this->offset = null; |
326
|
|
|
// $command = $this->createFilter($db); |
327
|
|
|
// |
328
|
|
|
// $this->select = $select; |
329
|
|
|
// $this->limit = $limit; |
330
|
|
|
// $this->offset = $offset; |
331
|
|
|
// |
332
|
|
|
// if (empty($this->groupBy) && empty($this->having) && empty($this->union) && !$this->distinct) { |
333
|
|
|
// return $command->queryScalar(); |
334
|
|
|
// } else { |
335
|
|
|
// return (new Query)->select([$selectExpression]) |
336
|
|
|
// ->from(['c' => $this]) |
337
|
|
|
// ->createFilter($command->db) |
338
|
|
|
// ->queryScalar(); |
339
|
|
|
// } |
340
|
|
|
// } |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* Sets the SELECT part of the query. |
344
|
|
|
* @param string|array $columns the columns to be selected. |
345
|
|
|
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']). |
346
|
|
|
* |
347
|
|
|
* ```php |
348
|
|
|
* $query->addSelect(['cn, mail'])->one(); |
349
|
|
|
* ``` |
350
|
|
|
* |
351
|
|
|
* @return $this the query object itself |
352
|
|
|
*/ |
353
|
|
View Code Duplication |
public function select($columns) |
|
|
|
|
354
|
|
|
{ |
355
|
|
|
if (!is_array($columns)) { |
356
|
|
|
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); |
357
|
|
|
} |
358
|
|
|
$this->select = $columns; |
359
|
|
|
return $this; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* Add more columns to the select part of the query. |
364
|
|
|
* |
365
|
|
|
* ```php |
366
|
|
|
* $query->addSelect(['cn, mail'])->one(); |
367
|
|
|
* ``` |
368
|
|
|
* |
369
|
|
|
* @param string|array|Expression $columns the columns to add to the select. See [[select()]] for more |
370
|
|
|
* details about the format of this parameter. |
371
|
|
|
* @return $this the query object itself |
372
|
|
|
* @see select() |
373
|
|
|
*/ |
374
|
|
View Code Duplication |
public function addSelect($columns) |
|
|
|
|
375
|
|
|
{ |
376
|
|
|
if (!is_array($columns)) { |
377
|
|
|
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); |
378
|
|
|
} |
379
|
|
|
if ($this->select === null) { |
380
|
|
|
$this->select = $columns; |
381
|
|
|
} else { |
382
|
|
|
$this->select = array_merge($this->select, $columns); |
383
|
|
|
} |
384
|
|
|
return $this; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
/** |
388
|
|
|
* It sets the value indicating whether the search returns with Distinct or no filter |
389
|
|
|
* @param boolean $value whether to Search use Distinct or not. |
390
|
|
|
* @return $this the query object itself |
391
|
|
|
*/ |
392
|
|
|
public function distinct($value = true) |
393
|
|
|
{ |
394
|
|
|
$this->distinct = $value; |
395
|
|
|
return $this; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* Sets the FROM part of the query. |
400
|
|
|
* @param string|array $nodes the node(s) to be selected from. This can be either a string (e.g. `'user'`) |
401
|
|
|
* or an array (e.g. `['user', 'group']`) specifying one or several nodes names. |
402
|
|
|
* |
403
|
|
|
* @return $this the query object itself |
404
|
|
|
*/ |
405
|
|
View Code Duplication |
public function from($nodes) |
|
|
|
|
406
|
|
|
{ |
407
|
|
|
if (!is_array($nodes)) { |
408
|
|
|
$nodes = preg_split('/\s*,\s*/', trim($nodes), -1, PREG_SPLIT_NO_EMPTY); |
409
|
|
|
} |
410
|
|
|
$this->from = $nodes; |
411
|
|
|
return $this; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* Adds a filtering condition for a specific column and allow the user to choose a filter operator. |
416
|
|
|
* |
417
|
|
|
* It adds an additional WHERE condition for the given field and determines the comparison operator |
418
|
|
|
* based on the first few characters of the given value. |
419
|
|
|
* The condition is added in the same way as in [[andFilterWhere]] so [[isEmpty()|empty values]] are ignored. |
420
|
|
|
* The new condition and the existing one will be joined using the 'AND' operator. |
421
|
|
|
* |
422
|
|
|
* The comparison operator is intelligently determined based on the first few characters in the given value. |
423
|
|
|
* In particular, it recognizes the following operators if they appear as the leading characters in the given value: |
424
|
|
|
* |
425
|
|
|
* - `<`: the column must be less than the given value. |
426
|
|
|
* - `>`: the column must be greater than the given value. |
427
|
|
|
* - `<=`: the column must be less than or equal to the given value. |
428
|
|
|
* - `>=`: the column must be greater than or equal to the given value. |
429
|
|
|
* - `~=`: the column must approximate the given value. |
430
|
|
|
* - `=`: the column must be equal to the given value. |
431
|
|
|
* - If none of the above operators is detected, the `$defaultOperator` will be used. |
432
|
|
|
* |
433
|
|
|
* @param string $name the column name. |
434
|
|
|
* @param string $value the column value optionally prepended with the comparison operator. |
435
|
|
|
* @param string $defaultOperator The operator to use, when no operator is given in `$value`. |
436
|
|
|
* Defaults to `=`, performing an exact match. |
437
|
|
|
* @return $this The query object itself |
438
|
|
|
*/ |
439
|
|
|
public function andFilterCompare($name, $value, $defaultOperator = '=') |
440
|
|
|
{ |
441
|
|
|
if (preg_match("/^(~=|>=|>|<=|<|=)/", $value, $matches)) { |
442
|
|
|
$operator = $matches[1]; |
443
|
|
|
$value = substr($value, strlen($operator)); |
444
|
|
|
} else { |
445
|
|
|
$operator = $defaultOperator; |
446
|
|
|
} |
447
|
|
|
return $this->andFilterWhere([$operator, $name, $value]); |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Sets the GROUP BY part of the query. |
452
|
|
|
* @param string|array $columns the columns to be grouped by. |
453
|
|
|
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']). |
454
|
|
|
* |
455
|
|
|
* @return $this the query object itself |
456
|
|
|
* @see addGroupBy() |
457
|
|
|
*/ |
458
|
|
View Code Duplication |
public function groupBy($columns) |
|
|
|
|
459
|
|
|
{ |
460
|
|
|
if (!is_array($columns)) { |
461
|
|
|
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); |
462
|
|
|
} |
463
|
|
|
$this->groupBy = $columns; |
464
|
|
|
return $this; |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
/** |
468
|
|
|
* Adds additional group-by columns to the existing ones. |
469
|
|
|
* @param string|array $columns additional columns to be grouped by. |
470
|
|
|
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']). |
471
|
|
|
* |
472
|
|
|
* @return $this the query object itself |
473
|
|
|
* @see groupBy() |
474
|
|
|
*/ |
475
|
|
View Code Duplication |
public function addGroupBy($columns) |
|
|
|
|
476
|
|
|
{ |
477
|
|
|
if (!is_array($columns)) { |
478
|
|
|
$columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY); |
479
|
|
|
} |
480
|
|
|
if ($this->groupBy === null) { |
481
|
|
|
$this->groupBy = $columns; |
482
|
|
|
} else { |
483
|
|
|
$this->groupBy = array_merge($this->groupBy, $columns); |
484
|
|
|
} |
485
|
|
|
return $this; |
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
/** |
489
|
|
|
* Sets the HAVING part of the query. |
490
|
|
|
* @param string|array $condition the conditions to be put after HAVING. |
491
|
|
|
* Please refer to [[where()]] on how to specify this parameter. |
492
|
|
|
* @return $this the query object itself |
493
|
|
|
* @see andHaving() |
494
|
|
|
* @see orHaving() |
495
|
|
|
*/ |
496
|
|
|
public function having($condition) |
497
|
|
|
{ |
498
|
|
|
$this->having = $condition; |
499
|
|
|
return $this; |
500
|
|
|
} |
501
|
|
|
|
502
|
|
|
/** |
503
|
|
|
* Adds an additional HAVING condition to the existing one. |
504
|
|
|
* The new condition and the existing one will be joined using the 'AND' operator. |
505
|
|
|
* @param string|array $condition the new HAVING condition. Please refer to [[where()]] |
506
|
|
|
* on how to specify this parameter. |
507
|
|
|
* @return $this the query object itself |
508
|
|
|
* @see having() |
509
|
|
|
* @see orHaving() |
510
|
|
|
*/ |
511
|
|
View Code Duplication |
public function andHaving($condition) |
|
|
|
|
512
|
|
|
{ |
513
|
|
|
if ($this->having === null) { |
514
|
|
|
$this->having = $condition; |
515
|
|
|
} else { |
516
|
|
|
$this->having = ['and', $this->having, $condition]; |
517
|
|
|
} |
518
|
|
|
return $this; |
519
|
|
|
} |
520
|
|
|
|
521
|
|
|
/** |
522
|
|
|
* Adds an additional HAVING condition to the existing one. |
523
|
|
|
* The new condition and the existing one will be joined using the 'OR' operator. |
524
|
|
|
* @param string|array $condition the new HAVING condition. Please refer to [[where()]] |
525
|
|
|
* on how to specify this parameter. |
526
|
|
|
* @return $this the query object itself |
527
|
|
|
* @see having() |
528
|
|
|
* @see andHaving() |
529
|
|
|
*/ |
530
|
|
View Code Duplication |
public function orHaving($condition) |
|
|
|
|
531
|
|
|
{ |
532
|
|
|
if ($this->having === null) { |
533
|
|
|
$this->having = $condition; |
534
|
|
|
} else { |
535
|
|
|
$this->having = ['or', $this->having, $condition]; |
536
|
|
|
} |
537
|
|
|
return $this; |
538
|
|
|
} |
539
|
|
|
|
540
|
|
|
/** |
541
|
|
|
* Creates a new Query object and copies its property values from an existing one. |
542
|
|
|
* The properties being copies are the ones to be used by query builders. |
543
|
|
|
* @param Query $from the source query object |
544
|
|
|
* @return Query the new Query object |
545
|
|
|
*/ |
546
|
|
|
public static function create($from) |
547
|
|
|
{ |
548
|
|
|
return new self([ |
549
|
|
|
'where' => $from->where, |
550
|
|
|
'limit' => $from->limit, |
551
|
|
|
'offset' => $from->offset, |
552
|
|
|
'orderBy' => $from->orderBy, |
553
|
|
|
'indexBy' => $from->indexBy, |
554
|
|
|
'select' => $from->select, |
555
|
|
|
'distinct' => $from->distinct, |
556
|
|
|
'from' => $from->from, |
557
|
|
|
'groupBy' => $from->groupBy, |
558
|
|
|
'having' => $from->having, |
559
|
|
|
]); |
560
|
|
|
} |
561
|
|
|
} |
562
|
|
|
|
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.