Completed
Push — master ( 3141eb...d06511 )
by Christopher
02:25
created

Query::createCommand()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
c 4
b 0
f 0
dl 0
loc 18
rs 9.4285
cc 3
eloc 11
nc 4
nop 1
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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));
0 ignored issues
show
Documentation introduced by
array_push($params, 1) is of type integer, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Bug introduced by
It seems like $db is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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);
0 ignored issues
show
Documentation Bug introduced by
The method queryScalar does not exist on object<chrmorandi\ldap\Query>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
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()));
0 ignored issues
show
Bug introduced by
The method getSql cannot be called on $command (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
Bug introduced by
The method setSql cannot be called on $command (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
306
        $command->bindValues($params);
0 ignored issues
show
Bug introduced by
The method bindValues cannot be called on $command (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
307
        return (boolean)$command->queryScalar();
0 ignored issues
show
Bug introduced by
The method queryScalar cannot be called on $command (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
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)
0 ignored issues
show
Unused Code Comprehensibility introduced by
51% of this comment could be valid code. Did you maybe forget this after debugging?

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.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
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