Completed
Push — master ( b4f1a5...4c1600 )
by Carsten
80:08 queued 71:13
created

framework/db/Query.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db;
9
10
use Yii;
11
use yii\base\Component;
12
use yii\base\InvalidArgumentException;
13
use yii\helpers\ArrayHelper;
14
use yii\base\InvalidConfigException;
15
16
/**
17
 * Query represents a SELECT SQL statement in a way that is independent of DBMS.
18
 *
19
 * Query provides a set of methods to facilitate the specification of different clauses
20
 * in a SELECT statement. These methods can be chained together.
21
 *
22
 * By calling [[createCommand()]], we can get a [[Command]] instance which can be further
23
 * used to perform/execute the DB query against a database.
24
 *
25
 * For example,
26
 *
27
 * ```php
28
 * $query = new Query;
29
 * // compose the query
30
 * $query->select('id, name')
31
 *     ->from('user')
32
 *     ->limit(10);
33
 * // build and execute the query
34
 * $rows = $query->all();
35
 * // alternatively, you can create DB command and execute it
36
 * $command = $query->createCommand();
37
 * // $command->sql returns the actual SQL
38
 * $rows = $command->queryAll();
39
 * ```
40
 *
41
 * Query internally uses the [[QueryBuilder]] class to generate the SQL statement.
42
 *
43
 * A more detailed usage guide on how to work with Query can be found in the [guide article on Query Builder](guide:db-query-builder).
44
 *
45
 * @property string[] $tablesUsedInFrom Table names indexed by aliases. This property is read-only.
46
 *
47
 * @author Qiang Xue <[email protected]>
48
 * @author Carsten Brandt <[email protected]>
49
 * @since 2.0
50
 */
51
class Query extends Component implements QueryInterface, ExpressionInterface
52
{
53
    use QueryTrait;
54
55
    /**
56
     * @var array the columns being selected. For example, `['id', 'name']`.
57
     * This is used to construct the SELECT clause in a SQL statement. If not set, it means selecting all columns.
58
     * @see select()
59
     */
60
    public $select;
61
    /**
62
     * @var string additional option that should be appended to the 'SELECT' keyword. For example,
63
     * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
64
     */
65
    public $selectOption;
66
    /**
67
     * @var bool whether to select distinct rows of data only. If this is set true,
68
     * the SELECT clause would be changed to SELECT DISTINCT.
69
     */
70
    public $distinct;
71
    /**
72
     * @var array the table(s) to be selected from. For example, `['user', 'post']`.
73
     * This is used to construct the FROM clause in a SQL statement.
74
     * @see from()
75
     */
76
    public $from;
77
    /**
78
     * @var array how to group the query results. For example, `['company', 'department']`.
79
     * This is used to construct the GROUP BY clause in a SQL statement.
80
     */
81
    public $groupBy;
82
    /**
83
     * @var array how to join with other tables. Each array element represents the specification
84
     * of one join which has the following structure:
85
     *
86
     * ```php
87
     * [$joinType, $tableName, $joinCondition]
88
     * ```
89
     *
90
     * For example,
91
     *
92
     * ```php
93
     * [
94
     *     ['INNER JOIN', 'user', 'user.id = author_id'],
95
     *     ['LEFT JOIN', 'team', 'team.id = team_id'],
96
     * ]
97
     * ```
98
     */
99
    public $join;
100
    /**
101
     * @var string|array|ExpressionInterface the condition to be applied in the GROUP BY clause.
102
     * It can be either a string or an array. Please refer to [[where()]] on how to specify the condition.
103
     */
104
    public $having;
105
    /**
106
     * @var array this is used to construct the UNION clause(s) in a SQL statement.
107
     * Each array element is an array of the following structure:
108
     *
109
     * - `query`: either a string or a [[Query]] object representing a query
110
     * - `all`: boolean, whether it should be `UNION ALL` or `UNION`
111
     */
112
    public $union;
113
    /**
114
     * @var array list of query parameter values indexed by parameter placeholders.
115
     * For example, `[':name' => 'Dan', ':age' => 31]`.
116
     */
117
    public $params = [];
118
    /**
119
     * @var int|true the default number of seconds that query results can remain valid in cache.
120
     * Use 0 to indicate that the cached data will never expire.
121
     * Use a negative number to indicate that query cache should not be used.
122
     * Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.
123
     * @see cache()
124
     * @since 2.0.14
125
     */
126
    public $queryCacheDuration;
127
    /**
128
     * @var \yii\caching\Dependency the dependency to be associated with the cached query result for this query
129
     * @see cache()
130
     * @since 2.0.14
131
     */
132
    public $queryCacheDependency;
133
134
135
    /**
136
     * Creates a DB command that can be used to execute this query.
137
     * @param Connection $db the database connection used to generate the SQL statement.
138
     * If this parameter is not given, the `db` application component will be used.
139
     * @return Command the created DB command instance.
140
     */
141 363
    public function createCommand($db = null)
142
    {
143 363
        if ($db === null) {
144 34
            $db = Yii::$app->getDb();
145
        }
146 363
        list($sql, $params) = $db->getQueryBuilder()->build($this);
147
148 363
        $command = $db->createCommand($sql, $params);
149 363
        $this->setCommandCache($command);
150
151 363
        return $command;
152
    }
153
154
    /**
155
     * Prepares for building SQL.
156
     * This method is called by [[QueryBuilder]] when it starts to build SQL from a query object.
157
     * You may override this method to do some final preparation work when converting a query into a SQL statement.
158
     * @param QueryBuilder $builder
159
     * @return $this a prepared query instance which will be used by [[QueryBuilder]] to build the SQL
160
     */
161 777
    public function prepare($builder)
162
    {
163 777
        return $this;
164
    }
165
166
    /**
167
     * Starts a batch query.
168
     *
169
     * A batch query supports fetching data in batches, which can keep the memory usage under a limit.
170
     * This method will return a [[BatchQueryResult]] object which implements the [[\Iterator]] interface
171
     * and can be traversed to retrieve the data in batches.
172
     *
173
     * For example,
174
     *
175
     * ```php
176
     * $query = (new Query)->from('user');
177
     * foreach ($query->batch() as $rows) {
178
     *     // $rows is an array of 100 or fewer rows from user table
179
     * }
180
     * ```
181
     *
182
     * @param int $batchSize the number of records to be fetched in each batch.
183
     * @param Connection $db the database connection. If not set, the "db" application component will be used.
184
     * @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface
185
     * and can be traversed to retrieve the data in batches.
186
     */
187 6
    public function batch($batchSize = 100, $db = null)
188
    {
189 6
        return Yii::createObject([
190 6
            'class' => BatchQueryResult::className(),
191 6
            'query' => $this,
192 6
            'batchSize' => $batchSize,
193 6
            'db' => $db,
194
            'each' => false,
195
        ]);
196
    }
197
198
    /**
199
     * Starts a batch query and retrieves data row by row.
200
     *
201
     * This method is similar to [[batch()]] except that in each iteration of the result,
202
     * only one row of data is returned. For example,
203
     *
204
     * ```php
205
     * $query = (new Query)->from('user');
206
     * foreach ($query->each() as $row) {
207
     * }
208
     * ```
209
     *
210
     * @param int $batchSize the number of records to be fetched in each batch.
211
     * @param Connection $db the database connection. If not set, the "db" application component will be used.
212
     * @return BatchQueryResult the batch query result. It implements the [[\Iterator]] interface
213
     * and can be traversed to retrieve the data in batches.
214
     */
215 3
    public function each($batchSize = 100, $db = null)
216
    {
217 3
        return Yii::createObject([
218 3
            'class' => BatchQueryResult::className(),
219 3
            'query' => $this,
220 3
            'batchSize' => $batchSize,
221 3
            'db' => $db,
222
            'each' => true,
223
        ]);
224
    }
225
226
    /**
227
     * Executes the query and returns all results as an array.
228
     * @param Connection $db the database connection used to generate the SQL statement.
229
     * If this parameter is not given, the `db` application component will be used.
230
     * @return array the query results. If the query results in nothing, an empty array will be returned.
231
     */
232 424
    public function all($db = null)
233
    {
234 424
        if ($this->emulateExecution) {
235 9
            return [];
236
        }
237 418
        $rows = $this->createCommand($db)->queryAll();
238 418
        return $this->populate($rows);
239
    }
240
241
    /**
242
     * Converts the raw query results into the format as specified by this query.
243
     * This method is internally used to convert the data fetched from database
244
     * into the format as required by this query.
245
     * @param array $rows the raw query result from database
246
     * @return array the converted query result
247
     */
248 558
    public function populate($rows)
249
    {
250 558
        if ($this->indexBy === null) {
251 552
            return $rows;
252
        }
253 21
        $result = [];
254 21
        foreach ($rows as $row) {
255 21
            $result[ArrayHelper::getValue($row, $this->indexBy)] = $row;
256
        }
257
258 21
        return $result;
259
    }
260
261
    /**
262
     * Executes the query and returns a single row of result.
263
     * @param Connection $db the database connection used to generate the SQL statement.
264
     * If this parameter is not given, the `db` application component will be used.
265
     * @return array|bool the first row (in terms of an array) of the query result. False is returned if the query
266
     * results in nothing.
267
     */
268 461
    public function one($db = null)
269
    {
270 461
        if ($this->emulateExecution) {
271 6
            return false;
272
        }
273
274 455
        return $this->createCommand($db)->queryOne();
275
    }
276
277
    /**
278
     * Returns the query result as a scalar value.
279
     * The value returned will be the first column in the first row of the query results.
280
     * @param Connection $db the database connection used to generate the SQL statement.
281
     * If this parameter is not given, the `db` application component will be used.
282
     * @return string|null|false the value of the first column in the first row of the query result.
283
     * False is returned if the query result is empty.
284
     */
285 30
    public function scalar($db = null)
286
    {
287 30
        if ($this->emulateExecution) {
288 6
            return null;
289
        }
290
291 24
        return $this->createCommand($db)->queryScalar();
292
    }
293
294
    /**
295
     * Executes the query and returns the first column of the result.
296
     * @param Connection $db the database connection used to generate the SQL statement.
297
     * If this parameter is not given, the `db` application component will be used.
298
     * @return array the first column of the query result. An empty array is returned if the query results in nothing.
299
     */
300 73
    public function column($db = null)
301
    {
302 73
        if ($this->emulateExecution) {
303 6
            return [];
304
        }
305
306 67
        if ($this->indexBy === null) {
307 61
            return $this->createCommand($db)->queryColumn();
308
        }
309
310 9
        if (is_string($this->indexBy) && is_array($this->select) && count($this->select) === 1) {
311 9
            if (strpos($this->indexBy, '.') === false && count($tables = $this->getTablesUsedInFrom()) > 0) {
312 9
                $this->select[] = key($tables) . '.' . $this->indexBy;
313
            } else {
314
                $this->select[] = $this->indexBy;
315
            }
316
        }
317 9
        $rows = $this->createCommand($db)->queryAll();
318 9
        $results = [];
319 9
        foreach ($rows as $row) {
320 9
            $value = reset($row);
321
322 9
            if ($this->indexBy instanceof \Closure) {
323 3
                $results[call_user_func($this->indexBy, $row)] = $value;
324
            } else {
325 9
                $results[$row[$this->indexBy]] = $value;
326
            }
327
        }
328
329 9
        return $results;
330
    }
331
332
    /**
333
     * Returns the number of records.
334
     * @param string $q the COUNT expression. Defaults to '*'.
335
     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
336
     * @param Connection $db the database connection used to generate the SQL statement.
337
     * If this parameter is not given (or null), the `db` application component will be used.
338
     * @return int|string number of records. The result may be a string depending on the
339
     * underlying database engine and to support integer values higher than a 32bit PHP integer can handle.
340
     */
341 87
    public function count($q = '*', $db = null)
342
    {
343 87
        if ($this->emulateExecution) {
344 6
            return 0;
345
        }
346
347 87
        return $this->queryScalar("COUNT($q)", $db);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression $this->queryScalar("COUNT({$q})", $db); of type null|string|false adds false to the return on line 347 which is incompatible with the return type declared by the interface yii\db\QueryInterface::count of type integer. It seems like you forgot to handle an error condition.
Loading history...
348
    }
349
350
    /**
351
     * Returns the sum of the specified column values.
352
     * @param string $q the column name or expression.
353
     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
354
     * @param Connection $db the database connection used to generate the SQL statement.
355
     * If this parameter is not given, the `db` application component will be used.
356
     * @return mixed the sum of the specified column values.
357
     */
358 9
    public function sum($q, $db = null)
359
    {
360 9
        if ($this->emulateExecution) {
361 6
            return 0;
362
        }
363
364 3
        return $this->queryScalar("SUM($q)", $db);
365
    }
366
367
    /**
368
     * Returns the average of the specified column values.
369
     * @param string $q the column name or expression.
370
     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
371
     * @param Connection $db the database connection used to generate the SQL statement.
372
     * If this parameter is not given, the `db` application component will be used.
373
     * @return mixed the average of the specified column values.
374
     */
375 9
    public function average($q, $db = null)
376
    {
377 9
        if ($this->emulateExecution) {
378 6
            return 0;
379
        }
380
381 3
        return $this->queryScalar("AVG($q)", $db);
382
    }
383
384
    /**
385
     * Returns the minimum of the specified column values.
386
     * @param string $q the column name or expression.
387
     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
388
     * @param Connection $db the database connection used to generate the SQL statement.
389
     * If this parameter is not given, the `db` application component will be used.
390
     * @return mixed the minimum of the specified column values.
391
     */
392 9
    public function min($q, $db = null)
393
    {
394 9
        return $this->queryScalar("MIN($q)", $db);
395
    }
396
397
    /**
398
     * Returns the maximum of the specified column values.
399
     * @param string $q the column name or expression.
400
     * Make sure you properly [quote](guide:db-dao#quoting-table-and-column-names) column names in the expression.
401
     * @param Connection $db the database connection used to generate the SQL statement.
402
     * If this parameter is not given, the `db` application component will be used.
403
     * @return mixed the maximum of the specified column values.
404
     */
405 9
    public function max($q, $db = null)
406
    {
407 9
        return $this->queryScalar("MAX($q)", $db);
408
    }
409
410
    /**
411
     * Returns a value indicating whether the query result contains any row of data.
412
     * @param Connection $db the database connection used to generate the SQL statement.
413
     * If this parameter is not given, the `db` application component will be used.
414
     * @return bool whether the query result contains any row of data.
415
     */
416 76
    public function exists($db = null)
417
    {
418 76
        if ($this->emulateExecution) {
419 6
            return false;
420
        }
421 70
        $command = $this->createCommand($db);
422 70
        $params = $command->params;
423 70
        $command->setSql($command->db->getQueryBuilder()->selectExists($command->getSql()));
424 70
        $command->bindValues($params);
425 70
        return (bool) $command->queryScalar();
426
    }
427
428
    /**
429
     * Queries a scalar value by setting [[select]] first.
430
     * Restores the value of select to make this query reusable.
431
     * @param string|ExpressionInterface $selectExpression
432
     * @param Connection|null $db
433
     * @return bool|string
434
     */
435 87
    protected function queryScalar($selectExpression, $db)
436
    {
437 87
        if ($this->emulateExecution) {
438 6
            return null;
439
        }
440
441
        if (
442 87
            !$this->distinct
443 87
            && empty($this->groupBy)
444 87
            && empty($this->having)
445 87
            && empty($this->union)
446
        ) {
447 86
            $select = $this->select;
448 86
            $order = $this->orderBy;
449 86
            $limit = $this->limit;
450 86
            $offset = $this->offset;
451
452 86
            $this->select = [$selectExpression];
453 86
            $this->orderBy = null;
454 86
            $this->limit = null;
455 86
            $this->offset = null;
456 86
            $command = $this->createCommand($db);
457
458 86
            $this->select = $select;
459 86
            $this->orderBy = $order;
460 86
            $this->limit = $limit;
461 86
            $this->offset = $offset;
462
463 86
            return $command->queryScalar();
464
        }
465
466 7
        $command = (new self())
467 7
            ->select([$selectExpression])
468 7
            ->from(['c' => $this])
469 7
            ->createCommand($db);
470 7
        $this->setCommandCache($command);
471
472 7
        return $command->queryScalar();
473
    }
474
475
    /**
476
     * Returns table names used in [[from]] indexed by aliases.
477
     * Both aliases and names are enclosed into {{ and }}.
478
     * @return string[] table names indexed by aliases
479
     * @throws \yii\base\InvalidConfigException
480
     * @since 2.0.12
481
     */
482 69
    public function getTablesUsedInFrom()
483
    {
484 69
        if (empty($this->from)) {
485
            return [];
486
        }
487
488 69
        if (is_array($this->from)) {
489 33
            $tableNames = $this->from;
490 36
        } elseif (is_string($this->from)) {
491 24
            $tableNames = preg_split('/\s*,\s*/', trim($this->from), -1, PREG_SPLIT_NO_EMPTY);
492 12
        } elseif ($this->from instanceof Expression) {
493 6
            $tableNames = [$this->from];
494
        } else {
495 6
            throw new InvalidConfigException(gettype($this->from) . ' in $from is not supported.');
496
        }
497
498 63
        return $this->cleanUpTableNames($tableNames);
499
    }
500
501
    /**
502
     * Clean up table names and aliases
503
     * Both aliases and names are enclosed into {{ and }}.
504
     * @param array $tableNames non-empty array
505
     * @return string[] table names indexed by aliases
506
     * @since 2.0.14
507
     */
508 166
    protected function cleanUpTableNames($tableNames)
509
    {
510 166
        $cleanedUpTableNames = [];
511 166
        foreach ($tableNames as $alias => $tableName) {
512 166
            if (is_string($tableName) && !is_string($alias)) {
513
                $pattern = <<<PATTERN
514 145
~
515
^
516
\s*
517
(
518
(?:['"`\[]|{{)
519
.*?
520
(?:['"`\]]|}})
521
|
522
\(.*?\)
523
|
524
.*?
525
)
526
(?:
527
(?:
528
    \s+
529
    (?:as)?
530
    \s*
531
)
532
(
533
   (?:['"`\[]|{{)
534
    .*?
535
    (?:['"`\]]|}})
536
    |
537
    .*?
538
)
539
)?
540
\s*
541
$
542
~iux
543
PATTERN;
544 145
                if (preg_match($pattern, $tableName, $matches)) {
545 145
                    if (isset($matches[2])) {
546 18
                        list(, $tableName, $alias) = $matches;
547
                    } else {
548 139
                        $tableName = $alias = $matches[1];
549
                    }
550
                }
551
            }
552
553
554 166
            if ($tableName instanceof Expression) {
555 12
                if (!is_string($alias)) {
556 6
                    throw new InvalidArgumentException('To use Expression in from() method, pass it in array format with alias.');
557
                }
558 6
                $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;
559 154
            } elseif ($tableName instanceof self) {
560 6
                $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $tableName;
561
            } else {
562 160
                $cleanedUpTableNames[$this->ensureNameQuoted($alias)] = $this->ensureNameQuoted($tableName);
563
            }
564
        }
565
566 160
        return $cleanedUpTableNames;
567
    }
568
569
    /**
570
     * Ensures name is wrapped with {{ and }}
571
     * @param string $name
572
     * @return string
573
     */
574 160
    private function ensureNameQuoted($name)
575
    {
576 160
        $name = str_replace(["'", '"', '`', '[', ']'], '', $name);
577 160
        if ($name && !preg_match('/^{{.*}}$/', $name)) {
578 148
            return '{{' . $name . '}}';
579
        }
580
581 30
        return $name;
582
    }
583
584
    /**
585
     * Sets the SELECT part of the query.
586
     * @param string|array|ExpressionInterface $columns the columns to be selected.
587
     * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
588
     * Columns can be prefixed with table names (e.g. "user.id") and/or contain column aliases (e.g. "user.id AS user_id").
589
     * The method will automatically quote the column names unless a column contains some parenthesis
590
     * (which means the column contains a DB expression). A DB expression may also be passed in form of
591
     * an [[ExpressionInterface]] object.
592
     *
593
     * Note that if you are selecting an expression like `CONCAT(first_name, ' ', last_name)`, you should
594
     * use an array to specify the columns. Otherwise, the expression may be incorrectly split into several parts.
595
     *
596
     * When the columns are specified as an array, you may also use array keys as the column aliases (if a column
597
     * does not need alias, do not use a string key).
598
     *
599
     * Starting from version 2.0.1, you may also select sub-queries as columns by specifying each such column
600
     * as a `Query` instance representing the sub-query.
601
     *
602
     * @param string $option additional option that should be appended to the 'SELECT' keyword. For example,
603
     * in MySQL, the option 'SQL_CALC_FOUND_ROWS' can be used.
604
     * @return $this the query object itself
605
     */
606 399
    public function select($columns, $option = null)
607
    {
608 399
        if ($columns instanceof ExpressionInterface) {
609 3
            $columns = [$columns];
610 396
        } elseif (!is_array($columns)) {
611 107
            $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
612
        }
613
        // this sequantial assignment is needed in order to make sure select is being reset
614
        // before using getUniqueColumns() that checks it
615 399
        $this->select = [];
616 399
        $this->select = $this->getUniqueColumns($columns);
617 399
        $this->selectOption = $option;
618 399
        return $this;
619
    }
620
621
    /**
622
     * Add more columns to the SELECT part of the query.
623
     *
624
     * Note, that if [[select]] has not been specified before, you should include `*` explicitly
625
     * if you want to select all remaining columns too:
626
     *
627
     * ```php
628
     * $query->addSelect(["*", "CONCAT(first_name, ' ', last_name) AS full_name"])->one();
629
     * ```
630
     *
631
     * @param string|array|ExpressionInterface $columns the columns to add to the select. See [[select()]] for more
632
     * details about the format of this parameter.
633
     * @return $this the query object itself
634
     * @see select()
635
     */
636 9
    public function addSelect($columns)
637
    {
638 9
        if ($columns instanceof ExpressionInterface) {
639 3
            $columns = [$columns];
640 9
        } elseif (!is_array($columns)) {
641 3
            $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
642
        }
643 9
        $columns = $this->getUniqueColumns($columns);
644 9
        if ($this->select === null) {
645 3
            $this->select = $columns;
646
        } else {
647 9
            $this->select = array_merge($this->select, $columns);
648
        }
649
650 9
        return $this;
651
    }
652
653
    /**
654
     * Returns unique column names excluding duplicates.
655
     * Columns to be removed:
656
     * - if column definition already present in SELECT part with same alias
657
     * - if column definition without alias already present in SELECT part without alias too
658
     * @param array $columns the columns to be merged to the select.
659
     * @since 2.0.14
660
     */
661 399
    protected function getUniqueColumns($columns)
662
    {
663 399
        $unaliasedColumns = $this->getUnaliasedColumnsFromSelect();
664
665 399
        $result = [];
666 399
        foreach ($columns as $columnAlias => $columnDefinition) {
667 396
            if (!$columnDefinition instanceof Query) {
668 396
                if (is_string($columnAlias)) {
669 47
                    $existsInSelect = isset($this->select[$columnAlias]) && $this->select[$columnAlias] === $columnDefinition;
670 47
                    if ($existsInSelect) {
671 47
                        continue;
672
                    }
673 391
                } elseif (is_int($columnAlias)) {
674 391
                    $existsInSelect = in_array($columnDefinition, $unaliasedColumns, true);
675 391
                    $existsInResultSet = in_array($columnDefinition, $result, true);
676 391
                    if ($existsInSelect || $existsInResultSet) {
677 3
                        continue;
678
                    }
679
                }
680
            }
681
682 396
            $result[$columnAlias] = $columnDefinition;
683
        }
684 399
        return $result;
685
    }
686
687
    /**
688
     * @return array List of columns without aliases from SELECT statement.
689
     * @since 2.0.14
690
     */
691 399
    protected function getUnaliasedColumnsFromSelect()
692
    {
693 399
        $result = [];
694 399
        if (is_array($this->select)) {
695 399
            foreach ($this->select as $name => $value) {
696 9
                if (is_int($name)) {
697 9
                    $result[] = $value;
698
                }
699
            }
700
        }
701 399
        return array_unique($result);
702
    }
703
704
    /**
705
     * Sets the value indicating whether to SELECT DISTINCT or not.
706
     * @param bool $value whether to SELECT DISTINCT or not.
707
     * @return $this the query object itself
708
     */
709 6
    public function distinct($value = true)
710
    {
711 6
        $this->distinct = $value;
712 6
        return $this;
713
    }
714
715
    /**
716
     * Sets the FROM part of the query.
717
     * @param string|array|ExpressionInterface $tables the table(s) to be selected from. This can be either a string (e.g. `'user'`)
718
     * or an array (e.g. `['user', 'profile']`) specifying one or several table names.
719
     * Table names can contain schema prefixes (e.g. `'public.user'`) and/or table aliases (e.g. `'user u'`).
720
     * The method will automatically quote the table names unless it contains some parenthesis
721
     * (which means the table is given as a sub-query or DB expression).
722
     *
723
     * When the tables are specified as an array, you may also use the array keys as the table aliases
724
     * (if a table does not need alias, do not use a string key).
725
     *
726
     * Use a Query object to represent a sub-query. In this case, the corresponding array key will be used
727
     * as the alias for the sub-query.
728
     *
729
     * To specify the `FROM` part in plain SQL, you may pass an instance of [[ExpressionInterface]].
730
     *
731
     * Here are some examples:
732
     *
733
     * ```php
734
     * // SELECT * FROM  `user` `u`, `profile`;
735
     * $query = (new \yii\db\Query)->from(['u' => 'user', 'profile']);
736
     *
737
     * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;
738
     * $subquery = (new \yii\db\Query)->from('user')->where(['active' => true])
739
     * $query = (new \yii\db\Query)->from(['activeusers' => $subquery]);
740
     *
741
     * // subquery can also be a string with plain SQL wrapped in parenthesis
742
     * // SELECT * FROM (SELECT * FROM `user` WHERE `active` = 1) `activeusers`;
743
     * $subquery = "(SELECT * FROM `user` WHERE `active` = 1)";
744
     * $query = (new \yii\db\Query)->from(['activeusers' => $subquery]);
745
     * ```
746
     *
747
     * @return $this the query object itself
748
     */
749 438
    public function from($tables)
750
    {
751 438
        if ($tables instanceof Expression) {
752 6
            $tables = [$tables];
753
        }
754 438
        if (is_string($tables)) {
755 402
            $tables = preg_split('/\s*,\s*/', trim($tables), -1, PREG_SPLIT_NO_EMPTY);
756
        }
757 438
        $this->from = $tables;
758 438
        return $this;
759
    }
760
761
    /**
762
     * Sets the WHERE part of the query.
763
     *
764
     * The method requires a `$condition` parameter, and optionally a `$params` parameter
765
     * specifying the values to be bound to the query.
766
     *
767
     * The `$condition` parameter should be either a string (e.g. `'id=1'`) or an array.
768
     *
769
     * {@inheritdoc}
770
     *
771
     * @param string|array|ExpressionInterface $condition the conditions that should be put in the WHERE part.
772
     * @param array $params the parameters (name => value) to be bound to the query.
773
     * @return $this the query object itself
774
     * @see andWhere()
775
     * @see orWhere()
776
     * @see QueryInterface::where()
777
     */
778 750
    public function where($condition, $params = [])
779
    {
780 750
        $this->where = $condition;
781 750
        $this->addParams($params);
782 750
        return $this;
783
    }
784
785
    /**
786
     * Adds an additional WHERE condition to the existing one.
787
     * The new condition and the existing one will be joined using the `AND` operator.
788
     * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
789
     * on how to specify this parameter.
790
     * @param array $params the parameters (name => value) to be bound to the query.
791
     * @return $this the query object itself
792
     * @see where()
793
     * @see orWhere()
794
     */
795 348
    public function andWhere($condition, $params = [])
796
    {
797 348
        if ($this->where === null) {
798 293
            $this->where = $condition;
799 106
        } elseif (is_array($this->where) && isset($this->where[0]) && strcasecmp($this->where[0], 'and') === 0) {
800 38
            $this->where[] = $condition;
801
        } else {
802 106
            $this->where = ['and', $this->where, $condition];
803
        }
804 348
        $this->addParams($params);
805 348
        return $this;
806
    }
807
808
    /**
809
     * Adds an additional WHERE condition to the existing one.
810
     * The new condition and the existing one will be joined using the `OR` operator.
811
     * @param string|array|ExpressionInterface $condition the new WHERE condition. Please refer to [[where()]]
812
     * on how to specify this parameter.
813
     * @param array $params the parameters (name => value) to be bound to the query.
814
     * @return $this the query object itself
815
     * @see where()
816
     * @see andWhere()
817
     */
818 7
    public function orWhere($condition, $params = [])
819
    {
820 7
        if ($this->where === null) {
821
            $this->where = $condition;
822
        } else {
823 7
            $this->where = ['or', $this->where, $condition];
824
        }
825 7
        $this->addParams($params);
826 7
        return $this;
827
    }
828
829
    /**
830
     * Adds a filtering condition for a specific column and allow the user to choose a filter operator.
831
     *
832
     * It adds an additional WHERE condition for the given field and determines the comparison operator
833
     * based on the first few characters of the given value.
834
     * The condition is added in the same way as in [[andFilterWhere]] so [[isEmpty()|empty values]] are ignored.
835
     * The new condition and the existing one will be joined using the `AND` operator.
836
     *
837
     * The comparison operator is intelligently determined based on the first few characters in the given value.
838
     * In particular, it recognizes the following operators if they appear as the leading characters in the given value:
839
     *
840
     * - `<`: the column must be less than the given value.
841
     * - `>`: the column must be greater than the given value.
842
     * - `<=`: the column must be less than or equal to the given value.
843
     * - `>=`: the column must be greater than or equal to the given value.
844
     * - `<>`: the column must not be the same as the given value.
845
     * - `=`: the column must be equal to the given value.
846
     * - If none of the above operators is detected, the `$defaultOperator` will be used.
847
     *
848
     * @param string $name the column name.
849
     * @param string $value the column value optionally prepended with the comparison operator.
850
     * @param string $defaultOperator The operator to use, when no operator is given in `$value`.
851
     * Defaults to `=`, performing an exact match.
852
     * @return $this The query object itself
853
     * @since 2.0.8
854
     */
855 3
    public function andFilterCompare($name, $value, $defaultOperator = '=')
856
    {
857 3
        if (preg_match('/^(<>|>=|>|<=|<|=)/', $value, $matches)) {
858 3
            $operator = $matches[1];
859 3
            $value = substr($value, strlen($operator));
860
        } else {
861 3
            $operator = $defaultOperator;
862
        }
863
864 3
        return $this->andFilterWhere([$operator, $name, $value]);
865
    }
866
867
    /**
868
     * Appends a JOIN part to the query.
869
     * The first parameter specifies what type of join it is.
870
     * @param string $type the type of join, such as INNER JOIN, LEFT JOIN.
871
     * @param string|array $table the table to be joined.
872
     *
873
     * Use a string to represent the name of the table to be joined.
874
     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
875
     * The method will automatically quote the table name unless it contains some parenthesis
876
     * (which means the table is given as a sub-query or DB expression).
877
     *
878
     * Use an array to represent joining with a sub-query. The array must contain only one element.
879
     * The value must be a [[Query]] object representing the sub-query while the corresponding key
880
     * represents the alias for the sub-query.
881
     *
882
     * @param string|array $on the join condition that should appear in the ON part.
883
     * Please refer to [[where()]] on how to specify this parameter.
884
     *
885
     * Note that the array format of [[where()]] is designed to match columns to values instead of columns to columns, so
886
     * the following would **not** work as expected: `['post.author_id' => 'user.id']`, it would
887
     * match the `post.author_id` column value against the string `'user.id'`.
888
     * It is recommended to use the string syntax here which is more suited for a join:
889
     *
890
     * ```php
891
     * 'post.author_id = user.id'
892
     * ```
893
     *
894
     * @param array $params the parameters (name => value) to be bound to the query.
895
     * @return $this the query object itself
896
     */
897 48
    public function join($type, $table, $on = '', $params = [])
898
    {
899 48
        $this->join[] = [$type, $table, $on];
900 48
        return $this->addParams($params);
901
    }
902
903
    /**
904
     * Appends an INNER JOIN part to the query.
905
     * @param string|array $table the table to be joined.
906
     *
907
     * Use a string to represent the name of the table to be joined.
908
     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
909
     * The method will automatically quote the table name unless it contains some parenthesis
910
     * (which means the table is given as a sub-query or DB expression).
911
     *
912
     * Use an array to represent joining with a sub-query. The array must contain only one element.
913
     * The value must be a [[Query]] object representing the sub-query while the corresponding key
914
     * represents the alias for the sub-query.
915
     *
916
     * @param string|array $on the join condition that should appear in the ON part.
917
     * Please refer to [[join()]] on how to specify this parameter.
918
     * @param array $params the parameters (name => value) to be bound to the query.
919
     * @return $this the query object itself
920
     */
921 3
    public function innerJoin($table, $on = '', $params = [])
922
    {
923 3
        $this->join[] = ['INNER JOIN', $table, $on];
924 3
        return $this->addParams($params);
925
    }
926
927
    /**
928
     * Appends a LEFT OUTER JOIN part to the query.
929
     * @param string|array $table the table to be joined.
930
     *
931
     * Use a string to represent the name of the table to be joined.
932
     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
933
     * The method will automatically quote the table name unless it contains some parenthesis
934
     * (which means the table is given as a sub-query or DB expression).
935
     *
936
     * Use an array to represent joining with a sub-query. The array must contain only one element.
937
     * The value must be a [[Query]] object representing the sub-query while the corresponding key
938
     * represents the alias for the sub-query.
939
     *
940
     * @param string|array $on the join condition that should appear in the ON part.
941
     * Please refer to [[join()]] on how to specify this parameter.
942
     * @param array $params the parameters (name => value) to be bound to the query
943
     * @return $this the query object itself
944
     */
945 3
    public function leftJoin($table, $on = '', $params = [])
946
    {
947 3
        $this->join[] = ['LEFT JOIN', $table, $on];
948 3
        return $this->addParams($params);
949
    }
950
951
    /**
952
     * Appends a RIGHT OUTER JOIN part to the query.
953
     * @param string|array $table the table to be joined.
954
     *
955
     * Use a string to represent the name of the table to be joined.
956
     * The table name can contain a schema prefix (e.g. 'public.user') and/or table alias (e.g. 'user u').
957
     * The method will automatically quote the table name unless it contains some parenthesis
958
     * (which means the table is given as a sub-query or DB expression).
959
     *
960
     * Use an array to represent joining with a sub-query. The array must contain only one element.
961
     * The value must be a [[Query]] object representing the sub-query while the corresponding key
962
     * represents the alias for the sub-query.
963
     *
964
     * @param string|array $on the join condition that should appear in the ON part.
965
     * Please refer to [[join()]] on how to specify this parameter.
966
     * @param array $params the parameters (name => value) to be bound to the query
967
     * @return $this the query object itself
968
     */
969
    public function rightJoin($table, $on = '', $params = [])
970
    {
971
        $this->join[] = ['RIGHT JOIN', $table, $on];
972
        return $this->addParams($params);
973
    }
974
975
    /**
976
     * Sets the GROUP BY part of the query.
977
     * @param string|array|ExpressionInterface $columns the columns to be grouped by.
978
     * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
979
     * The method will automatically quote the column names unless a column contains some parenthesis
980
     * (which means the column contains a DB expression).
981
     *
982
     * Note that if your group-by is an expression containing commas, you should always use an array
983
     * to represent the group-by information. Otherwise, the method will not be able to correctly determine
984
     * the group-by columns.
985
     *
986
     * Since version 2.0.7, an [[ExpressionInterface]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
987
     * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
988
     * @return $this the query object itself
989
     * @see addGroupBy()
990
     */
991 24
    public function groupBy($columns)
992
    {
993 24
        if ($columns instanceof ExpressionInterface) {
994 3
            $columns = [$columns];
995 24
        } elseif (!is_array($columns)) {
996 24
            $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
997
        }
998 24
        $this->groupBy = $columns;
999 24
        return $this;
1000
    }
1001
1002
    /**
1003
     * Adds additional group-by columns to the existing ones.
1004
     * @param string|array $columns additional columns to be grouped by.
1005
     * Columns can be specified in either a string (e.g. "id, name") or an array (e.g. ['id', 'name']).
1006
     * The method will automatically quote the column names unless a column contains some parenthesis
1007
     * (which means the column contains a DB expression).
1008
     *
1009
     * Note that if your group-by is an expression containing commas, you should always use an array
1010
     * to represent the group-by information. Otherwise, the method will not be able to correctly determine
1011
     * the group-by columns.
1012
     *
1013
     * Since version 2.0.7, an [[Expression]] object can be passed to specify the GROUP BY part explicitly in plain SQL.
1014
     * Since version 2.0.14, an [[ExpressionInterface]] object can be passed as well.
1015
     * @return $this the query object itself
1016
     * @see groupBy()
1017
     */
1018 3
    public function addGroupBy($columns)
1019
    {
1020 3
        if ($columns instanceof ExpressionInterface) {
1021
            $columns = [$columns];
1022 3
        } elseif (!is_array($columns)) {
1023 3
            $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
1024
        }
1025 3
        if ($this->groupBy === null) {
1026
            $this->groupBy = $columns;
1027
        } else {
1028 3
            $this->groupBy = array_merge($this->groupBy, $columns);
1029
        }
1030
1031 3
        return $this;
1032
    }
1033
1034
    /**
1035
     * Sets the HAVING part of the query.
1036
     * @param string|array|ExpressionInterface $condition the conditions to be put after HAVING.
1037
     * Please refer to [[where()]] on how to specify this parameter.
1038
     * @param array $params the parameters (name => value) to be bound to the query.
1039
     * @return $this the query object itself
1040
     * @see andHaving()
1041
     * @see orHaving()
1042
     */
1043 10
    public function having($condition, $params = [])
1044
    {
1045 10
        $this->having = $condition;
1046 10
        $this->addParams($params);
1047 10
        return $this;
1048
    }
1049
1050
    /**
1051
     * Adds an additional HAVING condition to the existing one.
1052
     * The new condition and the existing one will be joined using the `AND` operator.
1053
     * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
1054
     * on how to specify this parameter.
1055
     * @param array $params the parameters (name => value) to be bound to the query.
1056
     * @return $this the query object itself
1057
     * @see having()
1058
     * @see orHaving()
1059
     */
1060 3
    public function andHaving($condition, $params = [])
1061
    {
1062 3
        if ($this->having === null) {
1063
            $this->having = $condition;
1064
        } else {
1065 3
            $this->having = ['and', $this->having, $condition];
1066
        }
1067 3
        $this->addParams($params);
1068 3
        return $this;
1069
    }
1070
1071
    /**
1072
     * Adds an additional HAVING condition to the existing one.
1073
     * The new condition and the existing one will be joined using the `OR` operator.
1074
     * @param string|array|ExpressionInterface $condition the new HAVING condition. Please refer to [[where()]]
1075
     * on how to specify this parameter.
1076
     * @param array $params the parameters (name => value) to be bound to the query.
1077
     * @return $this the query object itself
1078
     * @see having()
1079
     * @see andHaving()
1080
     */
1081 3
    public function orHaving($condition, $params = [])
1082
    {
1083 3
        if ($this->having === null) {
1084
            $this->having = $condition;
1085
        } else {
1086 3
            $this->having = ['or', $this->having, $condition];
1087
        }
1088 3
        $this->addParams($params);
1089 3
        return $this;
1090
    }
1091
1092
    /**
1093
     * Sets the HAVING part of the query but ignores [[isEmpty()|empty operands]].
1094
     *
1095
     * This method is similar to [[having()]]. The main difference is that this method will
1096
     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
1097
     * for building query conditions based on filter values entered by users.
1098
     *
1099
     * The following code shows the difference between this method and [[having()]]:
1100
     *
1101
     * ```php
1102
     * // HAVING `age`=:age
1103
     * $query->filterHaving(['name' => null, 'age' => 20]);
1104
     * // HAVING `age`=:age
1105
     * $query->having(['age' => 20]);
1106
     * // HAVING `name` IS NULL AND `age`=:age
1107
     * $query->having(['name' => null, 'age' => 20]);
1108
     * ```
1109
     *
1110
     * Note that unlike [[having()]], you cannot pass binding parameters to this method.
1111
     *
1112
     * @param array $condition the conditions that should be put in the HAVING part.
1113
     * See [[having()]] on how to specify this parameter.
1114
     * @return $this the query object itself
1115
     * @see having()
1116
     * @see andFilterHaving()
1117
     * @see orFilterHaving()
1118
     * @since 2.0.11
1119
     */
1120 6
    public function filterHaving(array $condition)
1121
    {
1122 6
        $condition = $this->filterCondition($condition);
1123 6
        if ($condition !== []) {
1124 6
            $this->having($condition);
1125
        }
1126
1127 6
        return $this;
1128
    }
1129
1130
    /**
1131
     * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].
1132
     * The new condition and the existing one will be joined using the `AND` operator.
1133
     *
1134
     * This method is similar to [[andHaving()]]. The main difference is that this method will
1135
     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
1136
     * for building query conditions based on filter values entered by users.
1137
     *
1138
     * @param array $condition the new HAVING condition. Please refer to [[having()]]
1139
     * on how to specify this parameter.
1140
     * @return $this the query object itself
1141
     * @see filterHaving()
1142
     * @see orFilterHaving()
1143
     * @since 2.0.11
1144
     */
1145 6
    public function andFilterHaving(array $condition)
1146
    {
1147 6
        $condition = $this->filterCondition($condition);
1148 6
        if ($condition !== []) {
1149
            $this->andHaving($condition);
1150
        }
1151
1152 6
        return $this;
1153
    }
1154
1155
    /**
1156
     * Adds an additional HAVING condition to the existing one but ignores [[isEmpty()|empty operands]].
1157
     * The new condition and the existing one will be joined using the `OR` operator.
1158
     *
1159
     * This method is similar to [[orHaving()]]. The main difference is that this method will
1160
     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
1161
     * for building query conditions based on filter values entered by users.
1162
     *
1163
     * @param array $condition the new HAVING condition. Please refer to [[having()]]
1164
     * on how to specify this parameter.
1165
     * @return $this the query object itself
1166
     * @see filterHaving()
1167
     * @see andFilterHaving()
1168
     * @since 2.0.11
1169
     */
1170 6
    public function orFilterHaving(array $condition)
1171
    {
1172 6
        $condition = $this->filterCondition($condition);
1173 6
        if ($condition !== []) {
1174
            $this->orHaving($condition);
1175
        }
1176
1177 6
        return $this;
1178
    }
1179
1180
    /**
1181
     * Appends a SQL statement using UNION operator.
1182
     * @param string|Query $sql the SQL statement to be appended using UNION
1183
     * @param bool $all TRUE if using UNION ALL and FALSE if using UNION
1184
     * @return $this the query object itself
1185
     */
1186 10
    public function union($sql, $all = false)
1187
    {
1188 10
        $this->union[] = ['query' => $sql, 'all' => $all];
1189 10
        return $this;
1190
    }
1191
1192
    /**
1193
     * Sets the parameters to be bound to the query.
1194
     * @param array $params list of query parameter values indexed by parameter placeholders.
1195
     * For example, `[':name' => 'Dan', ':age' => 31]`.
1196
     * @return $this the query object itself
1197
     * @see addParams()
1198
     */
1199 6
    public function params($params)
1200
    {
1201 6
        $this->params = $params;
1202 6
        return $this;
1203
    }
1204
1205
    /**
1206
     * Adds additional parameters to be bound to the query.
1207
     * @param array $params list of query parameter values indexed by parameter placeholders.
1208
     * For example, `[':name' => 'Dan', ':age' => 31]`.
1209
     * @return $this the query object itself
1210
     * @see params()
1211
     */
1212 1013
    public function addParams($params)
1213
    {
1214 1013
        if (!empty($params)) {
1215 77
            if (empty($this->params)) {
1216 77
                $this->params = $params;
1217
            } else {
1218 6
                foreach ($params as $name => $value) {
1219 6
                    if (is_int($name)) {
1220
                        $this->params[] = $value;
1221
                    } else {
1222 6
                        $this->params[$name] = $value;
1223
                    }
1224
                }
1225
            }
1226
        }
1227
1228 1013
        return $this;
1229
    }
1230
1231
    /**
1232
     * Enables query cache for this Query.
1233
     * @param int|true $duration the number of seconds that query results can remain valid in cache.
1234
     * Use 0 to indicate that the cached data will never expire.
1235
     * Use a negative number to indicate that query cache should not be used.
1236
     * Use boolean `true` to indicate that [[Connection::queryCacheDuration]] should be used.
1237
     * Defaults to `true`.
1238
     * @param \yii\caching\Dependency $dependency the cache dependency associated with the cached result.
1239
     * @return $this the Query object itself
1240
     * @since 2.0.14
1241
     */
1242 3
    public function cache($duration = true, $dependency = null)
1243
    {
1244 3
        $this->queryCacheDuration = $duration;
1245 3
        $this->queryCacheDependency = $dependency;
1246 3
        return $this;
1247
    }
1248
1249
    /**
1250
     * Disables query cache for this Query.
1251
     * @return $this the Query object itself
1252
     * @since 2.0.14
1253
     */
1254 3
    public function noCache()
1255
    {
1256 3
        $this->queryCacheDuration = -1;
1257 3
        return $this;
1258
    }
1259
1260
    /**
1261
     * Sets $command cache, if this query has enabled caching.
1262
     *
1263
     * @param Command $command
1264
     * @return Command
1265
     * @since 2.0.14
1266
     */
1267 754
    protected function setCommandCache($command)
1268
    {
1269 754
        if ($this->queryCacheDuration !== null || $this->queryCacheDependency !== null) {
1270 3
            $duration = $this->queryCacheDuration === true ? null : $this->queryCacheDuration;
1271 3
            $command->cache($duration, $this->queryCacheDependency);
1272
        }
1273
1274 754
        return $command;
1275
    }
1276
1277
    /**
1278
     * Creates a new Query object and copies its property values from an existing one.
1279
     * The properties being copies are the ones to be used by query builders.
1280
     * @param Query $from the source query object
1281
     * @return Query the new Query object
1282
     */
1283 394
    public static function create($from)
1284
    {
1285 394
        return new self([
1286 394
            'where' => $from->where,
1287 394
            'limit' => $from->limit,
1288 394
            'offset' => $from->offset,
1289 394
            'orderBy' => $from->orderBy,
1290 394
            'indexBy' => $from->indexBy,
1291 394
            'select' => $from->select,
1292 394
            'selectOption' => $from->selectOption,
1293 394
            'distinct' => $from->distinct,
1294 394
            'from' => $from->from,
1295 394
            'groupBy' => $from->groupBy,
1296 394
            'join' => $from->join,
1297 394
            'having' => $from->having,
1298 394
            'union' => $from->union,
1299 394
            'params' => $from->params,
1300
        ]);
1301
    }
1302
1303
    /**
1304
     * Returns the SQL representation of Query
1305
     * @return string
1306
     */
1307
    public function __toString()
1308
    {
1309
        return serialize($this);
1310
    }
1311
}
1312