Completed
Push — master ( 84dee3...084275 )
by Michal
36:03
created

Query::set()   C

Complexity

Conditions 7
Paths 8

Size

Total Lines 25
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 25
rs 6.7272
cc 7
eloc 15
nc 8
nop 3
1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11
 * @link          http://cakephp.org CakePHP(tm) Project
12
 * @since         3.0.0
13
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
14
 */
15
namespace Cake\Database;
16
17
use Cake\Database\Expression\OrderByExpression;
18
use Cake\Database\Expression\OrderClauseExpression;
19
use Cake\Database\Expression\QueryExpression;
20
use Cake\Database\Expression\ValuesExpression;
21
use Cake\Database\Statement\CallbackStatement;
22
use IteratorAggregate;
23
use RuntimeException;
24
25
/**
26
 * This class represents a Relational database SQL Query. A query can be of
27
 * different types like select, update, insert and delete. Exposes the methods
28
 * for dynamically constructing each query part, execute it and transform it
29
 * to a specific SQL dialect.
30
 */
31
class Query implements ExpressionInterface, IteratorAggregate
32
{
33
34
    use TypeMapTrait;
35
36
    /**
37
     * Connection instance to be used to execute this query.
38
     *
39
     * @var \Cake\Datasource\ConnectionInterface
40
     */
41
    protected $_connection;
42
43
    /**
44
     * Type of this query (select, insert, update, delete).
45
     *
46
     * @var string
47
     */
48
    protected $_type = 'select';
49
50
    /**
51
     * List of SQL parts that will be used to build this query.
52
     *
53
     * @var array
54
     */
55
    protected $_parts = [
56
        'delete' => true,
57
        'update' => [],
58
        'set' => [],
59
        'insert' => [],
60
        'values' => [],
61
        'select' => [],
62
        'distinct' => false,
63
        'modifier' => [],
64
        'from' => [],
65
        'join' => [],
66
        'where' => null,
67
        'group' => [],
68
        'having' => null,
69
        'order' => null,
70
        'limit' => null,
71
        'offset' => null,
72
        'union' => [],
73
        'epilog' => null
74
    ];
75
76
    /**
77
     * Indicates whether internal state of this query was changed, this is used to
78
     * discard internal cached objects such as the transformed query or the reference
79
     * to the executed statement.
80
     *
81
     * @var bool
82
     */
83
    protected $_dirty = false;
84
85
    /**
86
     * A list of callback functions to be called to alter each row from resulting
87
     * statement upon retrieval. Each one of the callback function will receive
88
     * the row array as first argument.
89
     *
90
     * @var array
91
     */
92
    protected $_resultDecorators = [];
93
94
    /**
95
     * Statement object resulting from executing this query.
96
     *
97
     * @var \Cake\Database\StatementInterface
98
     */
99
    protected $_iterator;
100
101
    /**
102
     * The object responsible for generating query placeholders and temporarily store values
103
     * associated to each of those.
104
     *
105
     * @var ValueBinder
106
     */
107
    protected $_valueBinder;
108
109
    /**
110
     * Instance of functions builder object used for generating arbitrary SQL functions.
111
     *
112
     * @var FunctionsBuilder
113
     */
114
    protected $_functionsBuilder;
115
116
    /**
117
     * Boolean for tracking whether or not buffered results
118
     * are enabled.
119
     *
120
     * @var bool
121
     */
122
    protected $_useBufferedResults = true;
123
124
    /**
125
     * Constructor.
126
     *
127
     * @param \Cake\Datasource\ConnectionInterface $connection The connection
128
     * object to be used for transforming and executing this query
129
     */
130
    public function __construct($connection)
131
    {
132
        $this->connection($connection);
133
    }
134
135
    /**
136
     * Sets the connection instance to be used for executing and transforming this query
137
     * When called with a null argument, it will return the current connection instance.
138
     *
139
     * @param \Cake\Datasource\ConnectionInterface $connection instance
140
     * @return $this|\Cake\Datasource\ConnectionInterface
141
     */
142
    public function connection($connection = null)
143
    {
144
        if ($connection === null) {
145
            return $this->_connection;
146
        }
147
        $this->_dirty();
148
        $this->_connection = $connection;
149
        return $this;
150
    }
151
152
    /**
153
     * Compiles the SQL representation of this query and executes it using the
154
     * configured connection object. Returns the resulting statement object.
155
     *
156
     * Executing a query internally executes several steps, the first one is
157
     * letting the connection transform this object to fit its particular dialect,
158
     * this might result in generating a different Query object that will be the one
159
     * to actually be executed. Immediately after, literal values are passed to the
160
     * connection so they are bound to the query in a safe way. Finally, the resulting
161
     * statement is decorated with custom objects to execute callbacks for each row
162
     * retrieved if necessary.
163
     *
164
     * Resulting statement is traversable, so it can be used in any loop as you would
165
     * with an array.
166
     *
167
     * This method can be overridden in query subclasses to decorate behavior
168
     * around query execution.
169
     *
170
     * @return \Cake\Database\StatementInterface
171
     */
172
    public function execute()
173
    {
174
        $statement = $this->_connection->run($this);
175
        $this->_iterator = $this->_decorateStatement($statement);
176
        $this->_dirty = false;
177
        return $this->_iterator;
178
    }
179
180
    /**
181
     * Returns the SQL representation of this object.
182
     *
183
     * This function will compile this query to make it compatible
184
     * with the SQL dialect that is used by the connection, This process might
185
     * add, remove or alter any query part or internal expression to make it
186
     * executable in the target platform.
187
     *
188
     * The resulting query may have placeholders that will be replaced with the actual
189
     * values when the query is executed, hence it is most suitable to use with
190
     * prepared statements.
191
     *
192
     * @param \Cake\Database\ValueBinder $generator A placeholder object that will hold
193
     * associated values for expressions
194
     * @return string
195
     */
196
    public function sql(ValueBinder $generator = null)
197
    {
198
        if (!$generator) {
199
            $generator = $this->valueBinder();
200
            $generator->resetCount();
0 ignored issues
show
Bug introduced by
The method resetCount does only exist in Cake\Database\ValueBinder, but not in Cake\Database\Query.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
201
        }
202
203
        return $this->connection()->compileQuery($this, $generator);
204
    }
205
206
    /**
207
     * Will iterate over every specified part. Traversing functions can aggregate
208
     * results using variables in the closure or instance variables. This function
209
     * is commonly used as a way for traversing all query parts that
210
     * are going to be used for constructing a query.
211
     *
212
     * The callback will receive 2 parameters, the first one is the value of the query
213
     * part that is being iterated and the second the name of such part.
214
     *
215
     * ### Example:
216
     * ```
217
     *  $query->select(['title'])->from('articles')->traverse(function ($value, $clause) {
218
     *      if ($clause === 'select') {
219
     *          var_dump($value);
220
     *      }
221
     *  }, ['select', 'from']);
222
     * ```
223
     *
224
     * @param callable $visitor a function or callable to be executed for each part
225
     * @param array $parts the query clauses to traverse
226
     * @return $this
227
     */
228
    public function traverse(callable $visitor, array $parts = [])
229
    {
230
        $parts = $parts ?: array_keys($this->_parts);
231
        foreach ($parts as $name) {
232
            $visitor($this->_parts[$name], $name);
233
        }
234
        return $this;
235
    }
236
237
    /**
238
     * Adds new fields to be returned by a SELECT statement when this query is
239
     * executed. Fields can be passed as an array of strings, array of expression
240
     * objects, a single expression or a single string.
241
     *
242
     * If an array is passed, keys will be used to alias fields using the value as the
243
     * real field to be aliased. It is possible to alias strings, Expression objects or
244
     * even other Query objects.
245
     *
246
     * If a callable function is passed, the returning array of the function will
247
     * be used as the list of fields.
248
     *
249
     * By default this function will append any passed argument to the list of fields
250
     * to be selected, unless the second argument is set to true.
251
     *
252
     * ### Examples:
253
     *
254
     * ```
255
     * $query->select(['id', 'title']); // Produces SELECT id, title
256
     * $query->select(['author' => 'author_id']); // Appends author: SELECT id, title, author_id as author
257
     * $query->select('id', true); // Resets the list: SELECT id
258
     * $query->select(['total' => $countQuery]); // SELECT id, (SELECT ...) AS total
259
     * $query->select(function ($query) {
260
     *     return ['article_id', 'total' => $query->count('*')];
261
     * })
262
     * ```
263
     *
264
     * By default no fields are selected, if you have an instance of `Cake\ORM\Query` and try to append
265
     * fields you should also call `Cake\ORM\Query::autoFields()` to select the default fields
266
     * from the table.
267
     *
268
     * @param array|ExpressionInterface|string|callable $fields fields to be added to the list.
269
     * @param bool $overwrite whether to reset fields with passed list or not
270
     * @return $this
271
     */
272
    public function select($fields = [], $overwrite = false)
273
    {
274
        if (!is_string($fields) && is_callable($fields)) {
275
            $fields = $fields($this);
276
        }
277
278
        if (!is_array($fields)) {
279
            $fields = [$fields];
280
        }
281
282 View Code Duplication
        if ($overwrite) {
283
            $this->_parts['select'] = $fields;
284
        } else {
285
            $this->_parts['select'] = array_merge($this->_parts['select'], $fields);
286
        }
287
288
        $this->_dirty();
289
        $this->_type = 'select';
290
        return $this;
291
    }
292
293
    /**
294
     * Adds a DISTINCT clause to the query to remove duplicates from the result set.
295
     * This clause can only be used for select statements.
296
     *
297
     * If you wish to filter duplicates based of those rows sharing a particular field
298
     * or set of fields, you may pass an array of fields to filter on. Beware that
299
     * this option might not be fully supported in all database systems.
300
     *
301
     * ### Examples:
302
     *
303
     * ```
304
     * // Filters products with the same name and city
305
     * $query->select(['name', 'city'])->from('products')->distinct();
306
     *
307
     * // Filters products in the same city
308
     * $query->distinct(['city']);
309
     * $query->distinct('city');
310
     *
311
     * // Filter products with the same name
312
     * $query->distinct(['name'], true);
313
     * $query->distinct('name', true);
314
     * ```
315
     *
316
     * @param array|ExpressionInterface|string|bool $on Enable/disable distinct class
317
     * or list of fields to be filtered on
318
     * @param bool $overwrite whether to reset fields with passed list or not
319
     * @return $this
320
     */
321
    public function distinct($on = [], $overwrite = false)
322
    {
323
        if ($on === []) {
324
            $on = true;
325
        } elseif (is_string($on)) {
326
            $on = [$on];
327
        }
328
329
        if (is_array($on)) {
330
            $merge = [];
331
            if (is_array($this->_parts['distinct'])) {
332
                $merge = $this->_parts['distinct'];
333
            }
334
            $on = ($overwrite) ? array_values($on) : array_merge($merge, array_values($on));
335
        }
336
337
        $this->_parts['distinct'] = $on;
338
        $this->_dirty();
339
        return $this;
340
    }
341
342
    /**
343
     * Adds a single or multiple SELECT modifiers to be used in the SELECT.
344
     *
345
     * By default this function will append any passed argument to the list of modifiers
346
     * to be applied, unless the second argument is set to true.
347
     *
348
     * ### Example:
349
     *
350
     * ```
351
     * // Ignore cache query in MySQL
352
     * $query->select(['name', 'city'])->from('products')->modifier('SQL_NO_CACHE');
353
     * // It will produce the SQL: SELECT SQL_NO_CACHE name, city FROM products
354
     *
355
     * // Or with multiple modifiers
356
     * $query->select(['name', 'city'])->from('products')->modifier(['HIGH_PRIORITY', 'SQL_NO_CACHE']);
357
     * // It will produce the SQL: SELECT HIGH_PRIORITY SQL_NO_CACHE name, city FROM products
358
     * ```
359
     *
360
     * @param array|ExpressionInterface|string $modifiers modifiers to be applied to the query
361
     * @param bool $overwrite whether to reset order with field list or not
362
     * @return $this
363
     */
364
    public function modifier($modifiers, $overwrite = false)
365
    {
366
        $this->_dirty();
367
        if ($overwrite) {
368
            $this->_parts['modifier'] = [];
369
        }
370
        $this->_parts['modifier'] = array_merge($this->_parts['modifier'], (array)$modifiers);
371
        return $this;
372
    }
373
374
    /**
375
     * Adds a single or multiple tables to be used in the FROM clause for this query.
376
     * Tables can be passed as an array of strings, array of expression
377
     * objects, a single expression or a single string.
378
     *
379
     * If an array is passed, keys will be used to alias tables using the value as the
380
     * real field to be aliased. It is possible to alias strings, ExpressionInterface objects or
381
     * even other Query objects.
382
     *
383
     * By default this function will append any passed argument to the list of tables
384
     * to be selected from, unless the second argument is set to true.
385
     *
386
     * This method can be used for select, update and delete statements.
387
     *
388
     * ### Examples:
389
     *
390
     * ```
391
     *  $query->from(['p' => 'posts']); // Produces FROM posts p
392
     *  $query->from('authors'); // Appends authors: FROM posts p, authors
393
     *  $query->select(['products'], true); // Resets the list: FROM products
394
     *  $query->select(['sub' => $countQuery]); // FROM (SELECT ...) sub
395
     * ```
396
     *
397
     * @param array|ExpressionInterface|string $tables tables to be added to the list
398
     * @param bool $overwrite whether to reset tables with passed list or not
399
     * @return $this
400
     */
401
    public function from($tables = [], $overwrite = false)
402
    {
403
        if (empty($tables)) {
404
            return $this->_parts['from'];
405
        }
406
407
        if (is_string($tables)) {
408
            $tables = [$tables];
409
        }
410
411 View Code Duplication
        if ($overwrite) {
412
            $this->_parts['from'] = $tables;
413
        } else {
414
            $this->_parts['from'] = array_merge($this->_parts['from'], $tables);
415
        }
416
417
        $this->_dirty();
418
        return $this;
419
    }
420
421
    /**
422
     * Adds a single or multiple tables to be used as JOIN clauses to this query.
423
     * Tables can be passed as an array of strings, an array describing the
424
     * join parts, an array with multiple join descriptions, or a single string.
425
     *
426
     * By default this function will append any passed argument to the list of tables
427
     * to be joined, unless the third argument is set to true.
428
     *
429
     * When no join type is specified an INNER JOIN is used by default:
430
     * `$query->join(['authors'])` will produce `INNER JOIN authors ON 1 = 1`
431
     *
432
     * It is also possible to alias joins using the array key:
433
     * `$query->join(['a' => 'authors'])`` will produce `INNER JOIN authors a ON 1 = 1`
434
     *
435
     * A join can be fully described and aliased using the array notation:
436
     *
437
     * ```
438
     *  $query->join([
439
     *      'a' => [
440
     *          'table' => 'authors',
441
     *          'type' => 'LEFT',
442
     *          'conditions' => 'a.id = b.author_id'
443
     *      ]
444
     *  ]);
445
     *  // Produces LEFT JOIN authors a ON a.id = b.author_id
446
     * ```
447
     *
448
     * You can even specify multiple joins in an array, including the full description:
449
     *
450
     * ```
451
     *  $query->join([
452
     *      'a' => [
453
     *          'table' => 'authors',
454
     *          'type' => 'LEFT',
455
     *          'conditions' => 'a.id = b.author_id'
456
     *      ],
457
     *      'p' => [
458
     *          'table' => 'publishers',
459
     *          'type' => 'INNER',
460
     *          'conditions' => 'p.id = b.publisher_id AND p.name = "Cake Software Foundation"'
461
     *      ]
462
     *  ]);
463
     *  // LEFT JOIN authors a ON a.id = b.author_id
464
     *  // INNER JOIN publishers p ON p.id = b.publisher_id AND p.name = "Cake Software Foundation"
465
     * ```
466
     *
467
     * ### Using conditions and types
468
     *
469
     * Conditions can be expressed, as in the examples above, using a string for comparing
470
     * columns, or string with already quoted literal values. Additionally it is
471
     * possible to use conditions expressed in arrays or expression objects.
472
     *
473
     * When using arrays for expressing conditions, it is often desirable to convert
474
     * the literal values to the correct database representation. This is achieved
475
     * using the second parameter of this function.
476
     *
477
     * ```
478
     *  $query->join(['a' => [
479
     *      'table' => 'articles',
480
     *      'conditions' => [
481
     *          'a.posted >=' => new DateTime('-3 days'),
482
     *          'a.published' => true,
483
     *          'a.author_id = authors.id'
484
     *      ]
485
     *  ]], ['a.posted' => 'datetime', 'a.published' => 'boolean'])
486
     * ```
487
     *
488
     * ### Overwriting joins
489
     *
490
     * When creating aliased joins using the array notation, you can override
491
     * previous join definitions by using the same alias in consequent
492
     * calls to this function or you can replace all previously defined joins
493
     * with another list if the third parameter for this function is set to true.
494
     *
495
     * ```
496
     *  $query->join(['alias' => 'table']); // joins table with as alias
497
     *  $query->join(['alias' => 'another_table']); // joins another_table with as alias
498
     *  $query->join(['something' => 'different_table'], [], true); // resets joins list
499
     * ```
500
     *
501
     * @param array|string|null $tables list of tables to be joined in the query
502
     * @param array $types associative array of type names used to bind values to query
503
     * @param bool $overwrite whether to reset joins with passed list or not
504
     * @see \Cake\Database\Type
505
     * @return $this
506
     */
507
    public function join($tables = null, $types = [], $overwrite = false)
508
    {
509
        if ($tables === null) {
510
            return $this->_parts['join'];
511
        }
512
513
        if (is_string($tables) || isset($tables['table'])) {
514
            $tables = [$tables];
515
        }
516
517
        $joins = [];
518
        $i = count($this->_parts['join']);
519
        foreach ($tables as $alias => $t) {
520
            if (!is_array($t)) {
521
                $t = ['table' => $t, 'conditions' => $this->newExpr()];
522
            }
523
524
            if (!is_string($t['conditions']) && is_callable($t['conditions'])) {
525
                $t['conditions'] = $t['conditions']($this->newExpr(), $this);
526
            }
527
528
            if (!($t['conditions'] instanceof ExpressionInterface)) {
529
                $t['conditions'] = $this->newExpr()->add($t['conditions'], $types);
530
            }
531
            $alias = is_string($alias) ? $alias : null;
532
            $joins[$alias ?: $i++] = $t + ['type' => 'INNER', 'alias' => $alias];
533
        }
534
535 View Code Duplication
        if ($overwrite) {
536
            $this->_parts['join'] = $joins;
537
        } else {
538
            $this->_parts['join'] = array_merge($this->_parts['join'], $joins);
539
        }
540
541
        $this->_dirty();
542
        return $this;
543
    }
544
545
    /**
546
     * Remove a join if it has been defined.
547
     *
548
     * Useful when you are redefining joins or want to re-order
549
     * the join clauses.
550
     *
551
     * @param string $name The alias/name of the join to remove.
552
     * @return $this
553
     */
554
    public function removeJoin($name)
555
    {
556
        unset($this->_parts['join'][$name]);
557
        $this->_dirty();
558
        return $this;
559
    }
560
561
    /**
562
     * Adds a single LEFT JOIN clause to the query.
563
     *
564
     * This is a shorthand method for building joins via `join()`.
565
     *
566
     * The table name can be passed as a string, or as an array in case it needs to
567
     * be aliased:
568
     *
569
     * ```
570
     * // LEFT JOIN authors ON authors.id = posts.author_id
571
     * $query->leftJoin('authors', 'authors.id = posts.author_id');
572
     *
573
     * // LEFT JOIN authors a ON a.id = posts.author_id
574
     * $query->leftJoin(['a' => 'authors'], 'a.id = posts.author_id');
575
     * ```
576
     *
577
     * Conditions can be passed as strings, arrays, or expression objects. When
578
     * using arrays it is possible to combine them with the `$types` parameter
579
     * in order to define how to convert the values:
580
     *
581
     * ```
582
     * $query->leftJoin(['a' => 'articles'], [
583
     *      'a.posted >=' => new DateTime('-3 days'),
584
     *      'a.published' => true,
585
     *      'a.author_id = authors.id'
586
     * ], ['a.posted' => 'datetime', 'a.published' => 'boolean']);
587
     * ```
588
     *
589
     * See `join()` for further details on conditions and types.
590
     *
591
     * @param string|array $table The table to join with
592
     * @param string|array|\Cake\Database\ExpressionInterface $conditions The conditions
593
     * to use for joining.
594
     * @param array $types a list of types associated to the conditions used for converting
595
     * values to the corresponding database representation.
596
     * @return $this
597
     */
598
    public function leftJoin($table, $conditions = [], $types = [])
599
    {
600
        return $this->join($this->_makeJoin($table, $conditions, 'LEFT'), $types);
601
    }
602
603
    /**
604
     * Adds a single RIGHT JOIN clause to the query.
605
     *
606
     * This is a shorthand method for building joins via `join()`.
607
     *
608
     * The arguments of this method are identical to the `leftJoin()` shorthand, please refer
609
     * to that methods description for further details.
610
     *
611
     * @param string|array $table The table to join with
612
     * @param string|array|\Cake\Database\ExpressionInterface $conditions The conditions
613
     * to use for joining.
614
     * @param array $types a list of types associated to the conditions used for converting
615
     * values to the corresponding database representation.
616
     * @return $this
617
     */
618
    public function rightJoin($table, $conditions = [], $types = [])
619
    {
620
        return $this->join($this->_makeJoin($table, $conditions, 'RIGHT'), $types);
621
    }
622
623
    /**
624
     * Adds a single INNER JOIN clause to the query.
625
     *
626
     * This is a shorthand method for building joins via `join()`.
627
     *
628
     * The arguments of this method are identical to the `leftJoin()` shorthand, please refer
629
     * to that methods description for further details.
630
     *
631
     * @param string|array $table The table to join with
632
     * @param string|array|\Cake\Database\ExpressionInterface $conditions The conditions
633
     * to use for joining.
634
     * @param array $types a list of types associated to the conditions used for converting
635
     * values to the corresponding database representation.
636
     * @return $this
637
     */
638
    public function innerJoin($table, $conditions = [], $types = [])
639
    {
640
        return $this->join($this->_makeJoin($table, $conditions, 'INNER'), $types);
641
    }
642
643
    /**
644
     * Returns an array that can be passed to the join method describing a single join clause
645
     *
646
     * @param string|array $table The table to join with
647
     * @param string|array|\Cake\Database\ExpressionInterface $conditions The conditions
648
     * to use for joining.
649
     * @param string $type the join type to use
650
     * @return array
651
     */
652
    protected function _makeJoin($table, $conditions, $type)
653
    {
654
        $alias = $table;
655
656
        if (is_array($table)) {
657
            $alias = key($table);
658
            $table = current($table);
659
        }
660
661
        return [
662
            $alias => [
663
                'table' => $table,
664
                'conditions' => $conditions,
665
                'type' => $type
666
            ]
667
        ];
668
    }
669
670
    /**
671
     * Adds a condition or set of conditions to be used in the WHERE clause for this
672
     * query. Conditions can be expressed as an array of fields as keys with
673
     * comparison operators in it, the values for the array will be used for comparing
674
     * the field to such literal. Finally, conditions can be expressed as a single
675
     * string or an array of strings.
676
     *
677
     * When using arrays, each entry will be joined to the rest of the conditions using
678
     * an AND operator. Consecutive calls to this function will also join the new
679
     * conditions specified using the AND operator. Additionally, values can be
680
     * expressed using expression objects which can include other query objects.
681
     *
682
     * Any conditions created with this methods can be used with any SELECT, UPDATE
683
     * and DELETE type of queries.
684
     *
685
     * ### Conditions using operators:
686
     *
687
     * ```
688
     *  $query->where([
689
     *      'posted >=' => new DateTime('3 days ago'),
690
     *      'title LIKE' => 'Hello W%',
691
     *      'author_id' => 1,
692
     *  ], ['posted' => 'datetime']);
693
     * ```
694
     *
695
     * The previous example produces:
696
     *
697
     * `WHERE posted >= 2012-01-27 AND title LIKE 'Hello W%' AND author_id = 1`
698
     *
699
     * Second parameter is used to specify what type is expected for each passed
700
     * key. Valid types can be used from the mapped with Database\Type class.
701
     *
702
     * ### Nesting conditions with conjunctions:
703
     *
704
     * ```
705
     *  $query->where([
706
     *      'author_id !=' => 1,
707
     *      'OR' => ['published' => true, 'posted <' => new DateTime('now')],
708
     *      'NOT' => ['title' => 'Hello']
709
     *  ], ['published' => boolean, 'posted' => 'datetime']
710
     * ```
711
     *
712
     * The previous example produces:
713
     *
714
     * `WHERE author_id = 1 AND (published = 1 OR posted < '2012-02-01') AND NOT (title = 'Hello')`
715
     *
716
     * You can nest conditions using conjunctions as much as you like. Sometimes, you
717
     * may want to define 2 different options for the same key, in that case, you can
718
     * wrap each condition inside a new array:
719
     *
720
     * `$query->where(['OR' => [['published' => false], ['published' => true]])`
721
     *
722
     * Keep in mind that every time you call where() with the third param set to false
723
     * (default), it will join the passed conditions to the previous stored list using
724
     * the AND operator. Also, using the same array key twice in consecutive calls to
725
     * this method will not override the previous value.
726
     *
727
     * ### Using expressions objects:
728
     *
729
     * ```
730
     *  $exp = $query->newExpr()->add(['id !=' => 100, 'author_id' != 1])->type('OR');
731
     *  $query->where(['published' => true], ['published' => 'boolean'])->where($exp);
732
     * ```
733
     *
734
     * The previous example produces:
735
     *
736
     * `WHERE (id != 100 OR author_id != 1) AND published = 1`
737
     *
738
     * Other Query objects that be used as conditions for any field.
739
     *
740
     * ### Adding conditions in multiple steps:
741
     *
742
     * You can use callable functions to construct complex expressions, functions
743
     * receive as first argument a new QueryExpression object and this query instance
744
     * as second argument. Functions must return an expression object, that will be
745
     * added the list of conditions for the query using the AND operator.
746
     *
747
     * ```
748
     *  $query
749
     *  ->where(['title !=' => 'Hello World'])
750
     *  ->where(function ($exp, $query) {
751
     *      $or = $exp->or_(['id' => 1]);
752
     *      $and = $exp->and_(['id >' => 2, 'id <' => 10]);
753
     *  return $or->add($and);
754
     *  });
755
     * ```
756
     *
757
     * * The previous example produces:
758
     *
759
     * `WHERE title != 'Hello World' AND (id = 1 OR (id > 2 AND id < 10))`
760
     *
761
     * ### Conditions as strings:
762
     *
763
     * ```
764
     *  $query->where(['articles.author_id = authors.id', 'modified IS NULL']);
765
     * ```
766
     *
767
     * The previous example produces:
768
     *
769
     * `WHERE articles.author_id = authors.id AND modified IS NULL`
770
     *
771
     * Please note that when using the array notation or the expression objects, all
772
     * values will be correctly quoted and transformed to the correspondent database
773
     * data type automatically for you, thus securing your application from SQL injections.
774
     * If you use string conditions make sure that your values are correctly quoted.
775
     * The safest thing you can do is to never use string conditions.
776
     *
777
     * @param string|array|\Cake\Database\ExpressionInterface|callback|null $conditions The conditions to filter on.
778
     * @param array $types associative array of type names used to bind values to query
779
     * @param bool $overwrite whether to reset conditions with passed list or not
780
     * @see \Cake\Database\Type
781
     * @see \Cake\Database\Expression\QueryExpression
782
     * @return $this
783
     */
784 View Code Duplication
    public function where($conditions = null, $types = [], $overwrite = false)
785
    {
786
        if ($overwrite) {
787
            $this->_parts['where'] = $this->newExpr();
788
        }
789
        $this->_conjugate('where', $conditions, 'AND', $types);
790
        return $this;
791
    }
792
793
    /**
794
     * Connects any previously defined set of conditions to the provided list
795
     * using the AND operator. This function accepts the conditions list in the same
796
     * format as the method `where` does, hence you can use arrays, expression objects
797
     * callback functions or strings.
798
     *
799
     * It is important to notice that when calling this function, any previous set
800
     * of conditions defined for this query will be treated as a single argument for
801
     * the AND operator. This function will not only operate the most recently defined
802
     * condition, but all the conditions as a whole.
803
     *
804
     * When using an array for defining conditions, creating constraints form each
805
     * array entry will use the same logic as with the `where()` function. This means
806
     * that each array entry will be joined to the other using the AND operator, unless
807
     * you nest the conditions in the array using other operator.
808
     *
809
     * ### Examples:
810
     *
811
     * ```
812
     * $query->where(['title' => 'Hello World')->andWhere(['author_id' => 1]);
813
     * ```
814
     *
815
     * Will produce:
816
     *
817
     * `WHERE title = 'Hello World' AND author_id = 1`
818
     *
819
     * ```
820
     * $query
821
     *   ->where(['OR' => ['published' => false, 'published is NULL']])
822
     *   ->andWhere(['author_id' => 1, 'comments_count >' => 10])
823
     * ```
824
     *
825
     * Produces:
826
     *
827
     * `WHERE (published = 0 OR published IS NULL) AND author_id = 1 AND comments_count > 10`
828
     *
829
     * ```
830
     * $query
831
     *   ->where(['title' => 'Foo'])
832
     *   ->andWhere(function ($exp, $query) {
833
     *     return $exp
834
     *       ->add(['author_id' => 1])
835
     *       ->or_(['author_id' => 2]);
836
     *   });
837
     * ```
838
     *
839
     * Generates the following conditions:
840
     *
841
     * `WHERE (title = 'Foo') AND (author_id = 1 OR author_id = 2)`
842
     *
843
     * @param string|array|ExpressionInterface|callback $conditions The conditions to add with AND.
844
     * @param array $types associative array of type names used to bind values to query
845
     * @see \Cake\Database\Query::where()
846
     * @see \Cake\Database\Type
847
     * @return $this
848
     */
849
    public function andWhere($conditions, $types = [])
850
    {
851
        $this->_conjugate('where', $conditions, 'AND', $types);
852
        return $this;
853
    }
854
855
    /**
856
     * Connects any previously defined set of conditions to the provided list
857
     * using the OR operator. This function accepts the conditions list in the same
858
     * format as the method `where` does, hence you can use arrays, expression objects
859
     * callback functions or strings.
860
     *
861
     * It is important to notice that when calling this function, any previous set
862
     * of conditions defined for this query will be treated as a single argument for
863
     * the OR operator. This function will not only operate the most recently defined
864
     * condition, but all the conditions as a whole.
865
     *
866
     * When using an array for defining conditions, creating constraints form each
867
     * array entry will use the same logic as with the `where()` function. This means
868
     * that each array entry will be joined to the other using the OR operator, unless
869
     * you nest the conditions in the array using other operator.
870
     *
871
     * ### Examples:
872
     *
873
     * ```
874
     * $query->where(['title' => 'Hello World')->orWhere(['title' => 'Foo']);
875
     * ```
876
     *
877
     * Will produce:
878
     *
879
     * `WHERE title = 'Hello World' OR title = 'Foo'`
880
     *
881
     * ```
882
     * $query
883
     *   ->where(['OR' => ['published' => false, 'published is NULL']])
884
     *   ->orWhere(['author_id' => 1, 'comments_count >' => 10])
885
     * ```
886
     *
887
     * Produces:
888
     *
889
     * `WHERE (published = 0 OR published IS NULL) OR (author_id = 1 AND comments_count > 10)`
890
     *
891
     * ```
892
     * $query
893
     *   ->where(['title' => 'Foo'])
894
     *   ->orWhere(function ($exp, $query) {
895
     *     return $exp
896
     *       ->add(['author_id' => 1])
897
     *       ->or_(['author_id' => 2]);
898
     *   });
899
     * ```
900
     *
901
     * Generates the following conditions:
902
     *
903
     * `WHERE (title = 'Foo') OR (author_id = 1 OR author_id = 2)`
904
     *
905
     * @param string|array|ExpressionInterface|callback $conditions The conditions to add with OR.
906
     * @param array $types associative array of type names used to bind values to query
907
     * @see \Cake\Database\Query::where()
908
     * @see \Cake\Database\Type
909
     * @return $this
910
     */
911
    public function orWhere($conditions, $types = [])
912
    {
913
        $this->_conjugate('where', $conditions, 'OR', $types);
914
        return $this;
915
    }
916
917
    /**
918
     * Adds a single or multiple fields to be used in the ORDER clause for this query.
919
     * Fields can be passed as an array of strings, array of expression
920
     * objects, a single expression or a single string.
921
     *
922
     * If an array is passed, keys will be used as the field itself and the value will
923
     * represent the order in which such field should be ordered. When called multiple
924
     * times with the same fields as key, the last order definition will prevail over
925
     * the others.
926
     *
927
     * By default this function will append any passed argument to the list of fields
928
     * to be selected, unless the second argument is set to true.
929
     *
930
     * ### Examples:
931
     *
932
     * ```
933
     * $query->order(['title' => 'DESC', 'author_id' => 'ASC']);
934
     * ```
935
     *
936
     * Produces:
937
     *
938
     * `ORDER BY title DESC, author_id ASC`
939
     *
940
     * ```
941
     * $query->order(['title' => 'DESC NULLS FIRST'])->order('author_id');
942
     * ```
943
     *
944
     * Will generate:
945
     *
946
     * `ORDER BY title DESC NULLS FIRST, author_id`
947
     *
948
     * ```
949
     * $expression = $query->newExpr()->add(['id % 2 = 0']);
950
     * $query->order($expression)->order(['title' => 'ASC']);
951
     * ```
952
     *
953
     * Will become:
954
     *
955
     * `ORDER BY (id %2 = 0), title ASC`
956
     *
957
     * If you need to set complex expressions as order conditions, you
958
     * should use `orderAsc()` or `orderDesc()`.
959
     *
960
     * @param array|\Cake\Database\ExpressionInterface|string $fields fields to be added to the list
961
     * @param bool $overwrite whether to reset order with field list or not
962
     * @return $this
963
     */
964 View Code Duplication
    public function order($fields, $overwrite = false)
965
    {
966
        if ($overwrite) {
967
            $this->_parts['order'] = null;
968
        }
969
970
        if (!$fields) {
971
            return $this;
972
        }
973
974
        if (!$this->_parts['order']) {
975
            $this->_parts['order'] = new OrderByExpression();
976
        }
977
        $this->_conjugate('order', $fields, '', []);
978
        return $this;
979
    }
980
981
    /**
982
     * Add an ORDER BY clause with an ASC direction.
983
     *
984
     * This method allows you to set complex expressions
985
     * as order conditions unlike order()
986
     *
987
     * @param string|\Cake\Database\Expression\QueryExpression $field The field to order on.
988
     * @param bool $overwrite Whether or not to reset the order clauses.
989
     * @return $this
990
     */
991 View Code Duplication
    public function orderAsc($field, $overwrite = false)
992
    {
993
        if ($overwrite) {
994
            $this->_parts['order'] = null;
995
        }
996
        if (!$field) {
997
            return $this;
998
        }
999
1000
        if (!$this->_parts['order']) {
1001
            $this->_parts['order'] = new OrderByExpression();
1002
        }
1003
        $this->_parts['order']->add(new OrderClauseExpression($field, 'ASC'));
1004
        return $this;
1005
    }
1006
1007
    /**
1008
     * Add an ORDER BY clause with an ASC direction.
1009
     *
1010
     * This method allows you to set complex expressions
1011
     * as order conditions unlike order()
1012
     *
1013
     * @param string|\Cake\Database\Expression\QueryExpression $field The field to order on.
1014
     * @param bool $overwrite Whether or not to reset the order clauses.
1015
     * @return $this
1016
     */
1017 View Code Duplication
    public function orderDesc($field, $overwrite = false)
1018
    {
1019
        if ($overwrite) {
1020
            $this->_parts['order'] = null;
1021
        }
1022
        if (!$field) {
1023
            return $this;
1024
        }
1025
1026
        if (!$this->_parts['order']) {
1027
            $this->_parts['order'] = new OrderByExpression();
1028
        }
1029
        $this->_parts['order']->add(new OrderClauseExpression($field, 'DESC'));
1030
        return $this;
1031
    }
1032
1033
    /**
1034
     * Adds a single or multiple fields to be used in the GROUP BY clause for this query.
1035
     * Fields can be passed as an array of strings, array of expression
1036
     * objects, a single expression or a single string.
1037
     *
1038
     * By default this function will append any passed argument to the list of fields
1039
     * to be grouped, unless the second argument is set to true.
1040
     *
1041
     * ### Examples:
1042
     *
1043
     * ```
1044
     * // Produces GROUP BY id, title
1045
     * $query->group(['id', 'title']);
1046
     *
1047
     * // Produces GROUP BY title
1048
     * $query->group('title');
1049
     * ```
1050
     *
1051
     * @param array|ExpressionInterface|string $fields fields to be added to the list
1052
     * @param bool $overwrite whether to reset fields with passed list or not
1053
     * @return $this
1054
     */
1055
    public function group($fields, $overwrite = false)
1056
    {
1057
        if ($overwrite) {
1058
            $this->_parts['group'] = [];
1059
        }
1060
1061
        if (!is_array($fields)) {
1062
            $fields = [$fields];
1063
        }
1064
1065
        $this->_parts['group'] = array_merge($this->_parts['group'], array_values($fields));
1066
        $this->_dirty();
1067
        return $this;
1068
    }
1069
1070
    /**
1071
     * Adds a condition or set of conditions to be used in the HAVING clause for this
1072
     * query. This method operates in exactly the same way as the method `where()`
1073
     * does. Please refer to its documentation for an insight on how to using each
1074
     * parameter.
1075
     *
1076
     * @param string|array|ExpressionInterface|callback $conditions The having conditions.
1077
     * @param array $types associative array of type names used to bind values to query
1078
     * @param bool $overwrite whether to reset conditions with passed list or not
1079
     * @see \Cake\Database\Query::where()
1080
     * @return $this
1081
     */
1082 View Code Duplication
    public function having($conditions = null, $types = [], $overwrite = false)
1083
    {
1084
        if ($overwrite) {
1085
            $this->_parts['having'] = $this->newExpr();
1086
        }
1087
        $this->_conjugate('having', $conditions, 'AND', $types);
1088
        return $this;
1089
    }
1090
1091
    /**
1092
     * Connects any previously defined set of conditions to the provided list
1093
     * using the AND operator in the HAVING clause. This method operates in exactly
1094
     * the same way as the method `andWhere()` does. Please refer to its
1095
     * documentation for an insight on how to using each parameter.
1096
     *
1097
     * @param string|array|ExpressionInterface|callback $conditions The AND conditions for HAVING.
1098
     * @param array $types associative array of type names used to bind values to query
1099
     * @see \Cake\Database\Query::andWhere()
1100
     * @return $this
1101
     */
1102
    public function andHaving($conditions, $types = [])
1103
    {
1104
        $this->_conjugate('having', $conditions, 'AND', $types);
1105
        return $this;
1106
    }
1107
1108
    /**
1109
     * Connects any previously defined set of conditions to the provided list
1110
     * using the OR operator in the HAVING clause. This method operates in exactly
1111
     * the same way as the method `orWhere()` does. Please refer to its
1112
     * documentation for an insight on how to using each parameter.
1113
     *
1114
     * @param string|array|ExpressionInterface|callback $conditions The OR conditions for HAVING.
1115
     * @param array $types associative array of type names used to bind values to query.
1116
     * @see \Cake\Database\Query::orWhere()
1117
     * @return $this
1118
     */
1119
    public function orHaving($conditions, $types = [])
1120
    {
1121
        $this->_conjugate('having', $conditions, 'OR', $types);
1122
        return $this;
1123
    }
1124
1125
    /**
1126
     * Set the page of results you want.
1127
     *
1128
     * This method provides an easier to use interface to set the limit + offset
1129
     * in the record set you want as results. If empty the limit will default to
1130
     * the existing limit clause, and if that too is empty, then `25` will be used.
1131
     *
1132
     * Pages should start at 1.
1133
     *
1134
     * @param int $num The page number you want.
1135
     * @param int $limit The number of rows you want in the page. If null
1136
     *  the current limit clause will be used.
1137
     * @return $this
1138
     */
1139
    public function page($num, $limit = null)
1140
    {
1141
        if ($limit !== null) {
1142
            $this->limit($limit);
1143
        }
1144
        $limit = $this->clause('limit');
1145
        if ($limit === null) {
1146
            $limit = 25;
1147
            $this->limit($limit);
1148
        }
1149
        $offset = ($num - 1) * $limit;
1150
        if (PHP_INT_MAX <= $offset) {
1151
            $offset = PHP_INT_MAX;
1152
        }
1153
        $this->offset((int)$offset);
1154
        return $this;
1155
    }
1156
1157
    /**
1158
     * Sets the number of records that should be retrieved from database,
1159
     * accepts an integer or an expression object that evaluates to an integer.
1160
     * In some databases, this operation might not be supported or will require
1161
     * the query to be transformed in order to limit the result set size.
1162
     *
1163
     * ### Examples
1164
     *
1165
     * ```
1166
     * $query->limit(10) // generates LIMIT 10
1167
     * $query->limit($query->newExpr()->add(['1 + 1'])); // LIMIT (1 + 1)
1168
     * ```
1169
     *
1170
     * @param int|ExpressionInterface $num number of records to be returned
1171
     * @return $this
1172
     */
1173 View Code Duplication
    public function limit($num)
1174
    {
1175
        $this->_dirty();
1176
        if ($num !== null && !is_object($num)) {
1177
            $num = (int)$num;
1178
        }
1179
        $this->_parts['limit'] = $num;
1180
        return $this;
1181
    }
1182
1183
    /**
1184
     * Sets the number of records that should be skipped from the original result set
1185
     * This is commonly used for paginating large results. Accepts an integer or an
1186
     * expression object that evaluates to an integer.
1187
     *
1188
     * In some databases, this operation might not be supported or will require
1189
     * the query to be transformed in order to limit the result set size.
1190
     *
1191
     * ### Examples
1192
     *
1193
     * ```
1194
     *  $query->offset(10) // generates OFFSET 10
1195
     *  $query->offset($query->newExpr()->add(['1 + 1'])); // OFFSET (1 + 1)
1196
     * ```
1197
     *
1198
     * @param int|ExpressionInterface $num number of records to be skipped
1199
     * @return $this
1200
     */
1201 View Code Duplication
    public function offset($num)
1202
    {
1203
        $this->_dirty();
1204
        if ($num !== null && !is_object($num)) {
1205
            $num = (int)$num;
1206
        }
1207
        $this->_parts['offset'] = $num;
1208
        return $this;
1209
    }
1210
1211
    /**
1212
     * Adds a complete query to be used in conjunction with an UNION operator with
1213
     * this query. This is used to combine the result set of this query with the one
1214
     * that will be returned by the passed query. You can add as many queries as you
1215
     * required by calling multiple times this method with different queries.
1216
     *
1217
     * By default, the UNION operator will remove duplicate rows, if you wish to include
1218
     * every row for all queries, use unionAll().
1219
     *
1220
     * ### Examples
1221
     *
1222
     * ```
1223
     *  $union = (new Query($conn))->select(['id', 'title'])->from(['a' => 'articles']);
1224
     *  $query->select(['id', 'name'])->from(['d' => 'things'])->union($union);
1225
     * ```
1226
     *
1227
     * Will produce:
1228
     *
1229
     * `SELECT id, name FROM things d UNION SELECT id, title FROM articles a`
1230
     *
1231
     * @param string|Query $query full SQL query to be used in UNION operator
1232
     * @param bool $overwrite whether to reset the list of queries to be operated or not
1233
     * @return $this
1234
     */
1235 View Code Duplication
    public function union($query, $overwrite = false)
1236
    {
1237
        if ($overwrite) {
1238
            $this->_parts['union'] = [];
1239
        }
1240
        $this->_parts['union'][] = [
1241
            'all' => false,
1242
            'query' => $query
1243
        ];
1244
        $this->_dirty();
1245
        return $this;
1246
    }
1247
1248
    /**
1249
     * Adds a complete query to be used in conjunction with the UNION ALL operator with
1250
     * this query. This is used to combine the result set of this query with the one
1251
     * that will be returned by the passed query. You can add as many queries as you
1252
     * required by calling multiple times this method with different queries.
1253
     *
1254
     * Unlike UNION, UNION ALL will not remove duplicate rows.
1255
     *
1256
     * ```
1257
     * $union = (new Query($conn))->select(['id', 'title'])->from(['a' => 'articles']);
1258
     * $query->select(['id', 'name'])->from(['d' => 'things'])->unionAll($union);
1259
     * ```
1260
     *
1261
     * Will produce:
1262
     *
1263
     * `SELECT id, name FROM things d UNION ALL SELECT id, title FROM articles a`
1264
     *
1265
     * @param string|Query $query full SQL query to be used in UNION operator
1266
     * @param bool $overwrite whether to reset the list of queries to be operated or not
1267
     * @return $this
1268
     */
1269 View Code Duplication
    public function unionAll($query, $overwrite = false)
1270
    {
1271
        if ($overwrite) {
1272
            $this->_parts['union'] = [];
1273
        }
1274
        $this->_parts['union'][] = [
1275
            'all' => true,
1276
            'query' => $query
1277
        ];
1278
        $this->_dirty();
1279
        return $this;
1280
    }
1281
1282
    /**
1283
     * Create an insert query.
1284
     *
1285
     * Note calling this method will reset any data previously set
1286
     * with Query::values().
1287
     *
1288
     * @param array $columns The columns to insert into.
1289
     * @param array $types A map between columns & their datatypes.
1290
     * @return $this
1291
     * @throws \RuntimeException When there are 0 columns.
1292
     */
1293
    public function insert(array $columns, array $types = [])
1294
    {
1295
        if (empty($columns)) {
1296
            throw new RuntimeException('At least 1 column is required to perform an insert.');
1297
        }
1298
        $this->_dirty();
1299
        $this->_type = 'insert';
1300
        $this->_parts['insert'][1] = $columns;
1301
1302
        if (!$this->_parts['values']) {
1303
            $this->_parts['values'] = new ValuesExpression($columns, $this->typeMap()->types($types));
0 ignored issues
show
Bug introduced by
The method types does only exist in Cake\Database\TypeMap, but not in Cake\Database\TypeMapTrait.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1304
        }
1305
1306
        return $this;
1307
    }
1308
1309
    /**
1310
     * Set the table name for insert queries.
1311
     *
1312
     * @param string $table The table name to insert into.
1313
     * @return $this
1314
     */
1315 View Code Duplication
    public function into($table)
1316
    {
1317
        $this->_dirty();
1318
        $this->_type = 'insert';
1319
        $this->_parts['insert'][0] = $table;
1320
        return $this;
1321
    }
1322
1323
    /**
1324
     * Set the values for an insert query.
1325
     *
1326
     * Multi inserts can be performed by calling values() more than one time,
1327
     * or by providing an array of value sets. Additionally $data can be a Query
1328
     * instance to insert data from another SELECT statement.
1329
     *
1330
     * @param array|Query $data The data to insert.
1331
     * @return $this
1332
     * @throws \Cake\Database\Exception if you try to set values before declaring columns.
1333
     *   Or if you try to set values on non-insert queries.
1334
     */
1335
    public function values($data)
1336
    {
1337
        if ($this->_type !== 'insert') {
1338
            throw new Exception(
1339
                'You cannot add values before defining columns to use.'
1340
            );
1341
        }
1342
        if (empty($this->_parts['insert'])) {
1343
            throw new Exception(
1344
                'You cannot add values before defining columns to use.'
1345
            );
1346
        }
1347
1348
        $this->_dirty();
1349
        if ($data instanceof ValuesExpression) {
1350
            $this->_parts['values'] = $data;
1351
            return $this;
1352
        }
1353
1354
        $this->_parts['values']->add($data);
1355
        return $this;
1356
    }
1357
1358
    /**
1359
     * Create an update query.
1360
     *
1361
     * Can be combined with set() and where() methods to create update queries.
1362
     *
1363
     * @param string $table The table you want to update.
1364
     * @return $this
1365
     */
1366 View Code Duplication
    public function update($table)
1367
    {
1368
        $this->_dirty();
1369
        $this->_type = 'update';
1370
        $this->_parts['update'][0] = $table;
1371
        return $this;
1372
    }
1373
1374
    /**
1375
     * Set one or many fields to update.
1376
     *
1377
     * ### Examples
1378
     *
1379
     * Passing a string:
1380
     *
1381
     * ```
1382
     * $query->update('articles')->set('title', 'The Title');
1383
     * ```
1384
     *
1385
     * Passing an array:
1386
     *
1387
     * ```
1388
     * $query->update('articles')->set(['title' => 'The Title'], ['title' => 'string']);
1389
     * ```
1390
     *
1391
     * Passing a callable:
1392
     *
1393
     * ```
1394
     * $query->update('articles')->set(function ($exp) {
1395
     *  return $exp->eq('title', 'The title', 'string');
1396
     * });
1397
     * ```
1398
     *
1399
     * @param string|array|callable|QueryExpression $key The column name or array of keys
1400
     *    + values to set. This can also be a QueryExpression containing a SQL fragment.
1401
     *    It can also be a callable, that is required to return an expression object.
1402
     * @param mixed $value The value to update $key to. Can be null if $key is an
1403
     *    array or QueryExpression. When $key is an array, this parameter will be
1404
     *    used as $types instead.
1405
     * @param array $types The column types to treat data as.
1406
     * @return $this
1407
     */
1408
    public function set($key, $value = null, $types = [])
1409
    {
1410
        if (empty($this->_parts['set'])) {
1411
            $this->_parts['set'] = $this->newExpr()->type(',');
1412
        }
1413
1414
        if ($this->_parts['set']->isCallable($key)) {
1415
            $exp = $this->newExpr()->type(',');
1416
            $this->_parts['set']->add($key($exp));
1417
            return $this;
1418
        }
1419
1420
        if (is_array($key) || $key instanceof ExpressionInterface) {
1421
            $types = (array)$value;
1422
            $this->_parts['set']->add($key, $types);
1423
            return $this;
1424
        }
1425
1426
        if (is_string($types) && is_string($key)) {
1427
            $types = [$key => $types];
1428
        }
1429
        $this->_parts['set']->eq($key, $value, $types);
1430
1431
        return $this;
1432
    }
1433
1434
    /**
1435
     * Create a delete query.
1436
     *
1437
     * Can be combined with from(), where() and other methods to
1438
     * create delete queries with specific conditions.
1439
     *
1440
     * @param string $table The table to use when deleting.
1441
     * @return $this
1442
     */
1443
    public function delete($table = null)
1444
    {
1445
        $this->_dirty();
1446
        $this->_type = 'delete';
1447
        if ($table !== null) {
1448
            $this->from($table);
1449
        }
1450
        return $this;
1451
    }
1452
1453
    /**
1454
     * A string or expression that will be appended to the generated query
1455
     *
1456
     * ### Examples:
1457
     * ```
1458
     * $query->select('id')->where(['author_id' => 1])->epilog('FOR UPDATE');
1459
     * $query
1460
     *  ->insert('articles', ['title'])
1461
     *  ->values(['author_id' => 1])
1462
     *  ->epilog('RETURNING id');
1463
     * ```
1464
     *
1465
     * @param string|\Cake\Database\Expression\QueryExpression $expression The expression to be appended
1466
     * @return $this
1467
     */
1468
    public function epilog($expression = null)
1469
    {
1470
        $this->_dirty();
1471
        $this->_parts['epilog'] = $expression;
1472
        return $this;
1473
    }
1474
1475
    /**
1476
     * Returns the type of this query (select, insert, update, delete)
1477
     *
1478
     * @return string
1479
     */
1480
    public function type()
1481
    {
1482
        return $this->_type;
1483
    }
1484
1485
    /**
1486
     * Returns a new QueryExpression object. This is a handy function when
1487
     * building complex queries using a fluent interface. You can also override
1488
     * this function in subclasses to use a more specialized QueryExpression class
1489
     * if required.
1490
     *
1491
     * You can optionally pass a single raw SQL string or an array or expressions in
1492
     * any format accepted by \Cake\Database\Expression\QueryExpression:
1493
     *
1494
     * ```
1495
     * $expression = $query->newExpr(); // Returns an empty expression object
1496
     * $expression = $query->newExpr('Table.column = Table2.column'); // Return a raw SQL expression
1497
     * ```
1498
     *
1499
     * @param mixed $rawExpression A string, array or anything you want wrapped in an expression object
1500
     * @return \Cake\Database\Expression\QueryExpression
1501
     */
1502
    public function newExpr($rawExpression = null)
1503
    {
1504
        $expression = new QueryExpression([], $this->typeMap());
0 ignored issues
show
Bug introduced by
It seems like $this->typeMap() targeting Cake\Database\TypeMapTrait::typeMap() can also be of type object<Cake\Database\TypeMapTrait>; however, Cake\Database\Expression...pression::__construct() does only seem to accept array|object<Cake\Database\TypeMap>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
1505
1506
        if ($rawExpression !== null) {
1507
            $expression->add($rawExpression);
1508
        }
1509
1510
        return $expression;
1511
    }
1512
1513
    /**
1514
     * Returns an instance of a functions builder object that can be used for
1515
     * generating arbitrary SQL functions.
1516
     *
1517
     * ### Example:
1518
     *
1519
     * ```
1520
     * $query->func()->count('*');
1521
     * $query->func()->dateDiff(['2012-01-05', '2012-01-02'])
1522
     * ```
1523
     *
1524
     * @return \Cake\Database\FunctionsBuilder
1525
     */
1526
    public function func()
1527
    {
1528
        if (empty($this->_functionsBuilder)) {
1529
            $this->_functionsBuilder = new FunctionsBuilder;
1530
        }
1531
        return $this->_functionsBuilder;
1532
    }
1533
1534
    /**
1535
     * Executes this query and returns a results iterator. This function is required
1536
     * for implementing the IteratorAggregate interface and allows the query to be
1537
     * iterated without having to call execute() manually, thus making it look like
1538
     * a result set instead of the query itself.
1539
     *
1540
     * @return \Iterator
1541
     */
1542
    public function getIterator()
1543
    {
1544
        if (empty($this->_iterator) || $this->_dirty) {
1545
            $this->_iterator = $this->execute();
1546
        }
1547
        return $this->_iterator;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->_iterator; (Cake\Database\StatementInterface) is incompatible with the return type declared by the interface IteratorAggregate::getIterator of type Traversable.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1548
    }
1549
1550
    /**
1551
     * Returns any data that was stored in the specified clause. This is useful for
1552
     * modifying any internal part of the query and it is used by the SQL dialects
1553
     * to transform the query accordingly before it is executed. The valid clauses that
1554
     * can be retrieved are: delete, update, set, insert, values, select, distinct,
1555
     * from, join, set, where, group, having, order, limit, offset and union.
1556
     *
1557
     * The return value for each of those parts may vary. Some clauses use QueryExpression
1558
     * to internally store their state, some use arrays and others may use booleans or
1559
     * integers. This is summary of the return types for each clause.
1560
     *
1561
     * - update: string The name of the table to update
1562
     * - set: QueryExpression
1563
     * - insert: array, will return an array containing the table + columns.
1564
     * - values: ValuesExpression
1565
     * - select: array, will return empty array when no fields are set
1566
     * - distinct: boolean
1567
     * - from: array of tables
1568
     * - join: array
1569
     * - set: array
1570
     * - where: QueryExpression, returns null when not set
1571
     * - group: array
1572
     * - having: QueryExpression, returns null when not set
1573
     * - order: OrderByExpression, returns null when not set
1574
     * - limit: integer or QueryExpression, null when not set
1575
     * - offset: integer or QueryExpression, null when not set
1576
     * - union: array
1577
     *
1578
     * @param string $name name of the clause to be returned
1579
     * @return mixed
1580
     */
1581
    public function clause($name)
1582
    {
1583
        return $this->_parts[$name];
1584
    }
1585
1586
    /**
1587
     * Registers a callback to be executed for each result that is fetched from the
1588
     * result set, the callback function will receive as first parameter an array with
1589
     * the raw data from the database for every row that is fetched and must return the
1590
     * row with any possible modifications.
1591
     *
1592
     * Callbacks will be executed lazily, if only 3 rows are fetched for database it will
1593
     * called 3 times, event though there might be more rows to be fetched in the cursor.
1594
     *
1595
     * Callbacks are stacked in the order they are registered, if you wish to reset the stack
1596
     * the call this function with the second parameter set to true.
1597
     *
1598
     * If you wish to remove all decorators from the stack, set the first parameter
1599
     * to null and the second to true.
1600
     *
1601
     * ### Example
1602
     *
1603
     * ```
1604
     * $query->decorateResults(function ($row) {
1605
     *   $row['order_total'] = $row['subtotal'] + ($row['subtotal'] * $row['tax']);
1606
     *    return $row;
1607
     * });
1608
     * ```
1609
     *
1610
     * @param null|callable $callback The callback to invoke when results are fetched.
1611
     * @param bool $overwrite Whether or not this should append or replace all existing decorators.
1612
     * @return $this
1613
     */
1614
    public function decorateResults($callback, $overwrite = false)
1615
    {
1616
        if ($overwrite) {
1617
            $this->_resultDecorators = [];
1618
        }
1619
1620
        if ($callback !== null) {
1621
            $this->_resultDecorators[] = $callback;
1622
        }
1623
1624
        return $this;
1625
    }
1626
1627
    /**
1628
     * This function works similar to the traverse() function, with the difference
1629
     * that it does a full depth traversal of the entire expression tree. This will execute
1630
     * the provided callback function for each ExpressionInterface object that is
1631
     * stored inside this query at any nesting depth in any part of the query.
1632
     *
1633
     * Callback will receive as first parameter the currently visited expression.
1634
     *
1635
     * @param callable $callback the function to be executed for each ExpressionInterface
1636
     *   found inside this query.
1637
     * @return $this|null
1638
     */
1639
    public function traverseExpressions(callable $callback)
1640
    {
1641
        $visitor = function ($expression) use (&$visitor, $callback) {
1642
            if (is_array($expression)) {
1643
                foreach ($expression as $e) {
1644
                    $visitor($e);
1645
                }
1646
                return null;
1647
            }
1648
1649
            if ($expression instanceof ExpressionInterface) {
1650
                $expression->traverse($visitor);
1651
1652
                if (!($expression instanceof self)) {
1653
                    $callback($expression);
1654
                }
1655
            }
1656
        };
1657
        return $this->traverse($visitor);
1658
    }
1659
1660
    /**
1661
     * Associates a query placeholder to a value and a type.
1662
     *
1663
     * If type is expressed as "atype[]" (note braces) then it will cause the
1664
     * placeholder to be re-written dynamically so if the value is an array, it
1665
     * will create as many placeholders as values are in it. For example "string[]"
1666
     * will create several placeholders of type string.
1667
     *
1668
     * @param string|int $param placeholder to be replaced with quoted version
1669
     *   of $value
1670
     * @param mixed $value The value to be bound
1671
     * @param string|int $type the mapped type name, used for casting when sending
1672
     *   to database
1673
     * @return $this
1674
     */
1675
    public function bind($param, $value, $type = 'string')
1676
    {
1677
        $this->valueBinder()->bind($param, $value, $type);
1678
        return $this;
1679
    }
1680
1681
    /**
1682
     * Returns the currently used ValueBinder instance. If a value is passed,
1683
     * it will be set as the new instance to be used.
1684
     *
1685
     * A ValueBinder is responsible for generating query placeholders and temporarily
1686
     * associate values to those placeholders so that they can be passed correctly
1687
     * statement object.
1688
     *
1689
     * @param \Cake\Database\ValueBinder $binder new instance to be set. If no value is passed the
1690
     *   default one will be returned
1691
     * @return $this|\Cake\Database\ValueBinder
1692
     */
1693
    public function valueBinder($binder = null)
1694
    {
1695
        if ($binder === null) {
1696
            if ($this->_valueBinder === null) {
1697
                $this->_valueBinder = new ValueBinder;
1698
            }
1699
            return $this->_valueBinder;
1700
        }
1701
        $this->_valueBinder = $binder;
1702
        return $this;
1703
    }
1704
1705
    /**
1706
     * Enable/Disable buffered results.
1707
     *
1708
     * When enabled the results returned by this Query will be
1709
     * buffered. This enables you to iterate a result set multiple times, or
1710
     * both cache and iterate it.
1711
     *
1712
     * When disabled it will consume less memory as fetched results are not
1713
     * remembered for future iterations.
1714
     *
1715
     * If called with no arguments, it will return whether or not buffering is
1716
     * enabled.
1717
     *
1718
     * @param bool|null $enable whether or not to enable buffering
1719
     * @return bool|$this
1720
     */
1721
    public function bufferResults($enable = null)
1722
    {
1723
        if ($enable === null) {
1724
            return $this->_useBufferedResults;
1725
        }
1726
1727
        $this->_dirty();
1728
        $this->_useBufferedResults = (bool)$enable;
1729
        return $this;
1730
    }
1731
1732
    /**
1733
     * Auxiliary function used to wrap the original statement from the driver with
1734
     * any registered callbacks.
1735
     *
1736
     * @param \Cake\Database\StatementInterface $statement to be decorated
1737
     * @return \Cake\Database\Statement\CallbackStatement
1738
     */
1739
    protected function _decorateStatement($statement)
1740
    {
1741
        foreach ($this->_resultDecorators as $f) {
1742
            $statement = new CallbackStatement($statement, $this->connection()->driver(), $f);
1743
        }
1744
        return $statement;
1745
    }
1746
1747
    /**
1748
     * Helper function used to build conditions by composing QueryExpression objects.
1749
     *
1750
     * @param string $part Name of the query part to append the new part to
1751
     * @param string|null|array|ExpressionInterface|callback $append Expression or builder function to append.
1752
     * @param string $conjunction type of conjunction to be used to operate part
1753
     * @param array $types associative array of type names used to bind values to query
1754
     * @return void
1755
     */
1756
    protected function _conjugate($part, $append, $conjunction, $types)
1757
    {
1758
        $expression = $this->_parts[$part] ?: $this->newExpr();
1759
        if (empty($append)) {
1760
            $this->_parts[$part] = $expression;
1761
            return;
1762
        }
1763
1764
        if ($expression->isCallable($append)) {
1765
            $append = $append($this->newExpr(), $this);
1766
        }
1767
1768
        if ($expression->type() === $conjunction) {
1769
            $expression->add($append, $types);
1770
        } else {
1771
            $expression = $this->newExpr()
1772
                ->type($conjunction)
1773
                ->add([$append, $expression], $types);
1774
        }
1775
1776
        $this->_parts[$part] = $expression;
1777
        $this->_dirty();
1778
    }
1779
1780
    /**
1781
     * Marks a query as dirty, removing any preprocessed information
1782
     * from in memory caching.
1783
     *
1784
     * @return void
1785
     */
1786
    protected function _dirty()
1787
    {
1788
        $this->_dirty = true;
1789
1790
        if ($this->_iterator && $this->_valueBinder) {
1791
            $this->valueBinder()->reset();
0 ignored issues
show
Bug introduced by
The method reset does only exist in Cake\Database\ValueBinder, but not in Cake\Database\Query.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1792
        }
1793
    }
1794
1795
    /**
1796
     * Do a deep clone on this object.
1797
     *
1798
     * Will clone all of the expression objects used in
1799
     * each of the clauses, as well as the valueBinder.
1800
     *
1801
     * @return void
1802
     */
1803
    public function __clone()
1804
    {
1805
        $this->_iterator = null;
1806
        if ($this->_valueBinder) {
1807
            $this->_valueBinder = clone $this->_valueBinder;
1808
        }
1809
        foreach ($this->_parts as $name => $part) {
1810
            if (empty($part)) {
1811
                continue;
1812
            }
1813
            if (is_array($part)) {
1814
                foreach ($part as $i => $piece) {
1815
                    if ($piece instanceof ExpressionInterface) {
1816
                        $this->_parts[$name][$i] = clone $piece;
1817
                    }
1818
                }
1819
            }
1820
            if ($part instanceof ExpressionInterface) {
1821
                $this->_parts[$name] = clone $part;
1822
            }
1823
        }
1824
    }
1825
1826
    /**
1827
     * Returns string representation of this query (complete SQL statement).
1828
     *
1829
     * @return string
1830
     */
1831
    public function __toString()
1832
    {
1833
        return $this->sql();
1834
    }
1835
1836
    /**
1837
     * Returns an array that can be used to describe the internal state of this
1838
     * object.
1839
     *
1840
     * @return array
1841
     */
1842
    public function __debugInfo()
1843
    {
1844
        return [
1845
            '(help)' => 'This is a Query object, to get the results execute or iterate it.',
1846
            'sql' => $this->sql(),
1847
            'params' => $this->valueBinder()->bindings(),
0 ignored issues
show
Bug introduced by
The method bindings does only exist in Cake\Database\ValueBinder, but not in Cake\Database\Query.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
1848
            'defaultTypes' => $this->defaultTypes(),
1849
            'decorators' => count($this->_resultDecorators),
1850
            'executed' => $this->_iterator ? true : false
1851
        ];
1852
    }
1853
}
1854