Failed Conditions
Pull Request — 2.10 (#3762)
by Benjamin
09:16
created

QueryBuilder::resetQueryParts()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3.0123

Importance

Changes 0
Metric Value
eloc 5
c 0
b 0
f 0
dl 0
loc 11
ccs 8
cts 9
cp 0.8889
rs 10
cc 3
nc 4
nop 1
crap 3.0123
1
<?php
2
3
namespace Doctrine\DBAL\Query;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Driver\Statement;
7
use Doctrine\DBAL\ParameterType;
8
use Doctrine\DBAL\Query\Expression\CompositeExpression;
9
use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
10
use function array_key_exists;
11
use function array_keys;
12
use function array_unshift;
13
use function func_get_args;
14
use function func_num_args;
15
use function implode;
16
use function is_array;
17
use function is_object;
18
use function key;
19
use function strtoupper;
20
use function substr;
21
22
/**
23
 * QueryBuilder class is responsible to dynamically create SQL queries.
24
 *
25
 * Important: Verify that every feature you use will work with your database vendor.
26
 * SQL Query Builder does not attempt to validate the generated SQL at all.
27
 *
28
 * The query builder does no validation whatsoever if certain features even work with the
29
 * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements
30
 * even if some vendors such as MySQL support it.
31
 */
32
class QueryBuilder
33
{
34
    /*
35
     * The query types.
36
     */
37
    public const SELECT = 0;
38
    public const DELETE = 1;
39
    public const UPDATE = 2;
40
    public const INSERT = 3;
41
42
    /*
43
     * The builder states.
44
     */
45
    public const STATE_DIRTY = 0;
46
    public const STATE_CLEAN = 1;
47
48
    /**
49
     * The DBAL Connection.
50
     *
51
     * @var Connection
52
     */
53
    private $connection;
54
55
    /*
56
     * The default values of SQL parts collection
57
     */
58
    private const SQL_PARTS_DEFAULTS = [
59
        'select'   => [],
60
        'distinct' => false,
61
        'from'     => [],
62
        'join'     => [],
63
        'set'      => [],
64
        'where'    => null,
65
        'groupBy'  => [],
66
        'having'   => null,
67
        'orderBy'  => [],
68
        'values'   => [],
69
    ];
70
71
    /**
72
     * The array of SQL parts collected.
73
     *
74
     * @var mixed[]
75
     */
76
    private $sqlParts = self::SQL_PARTS_DEFAULTS;
77
78
    /**
79
     * The complete SQL string for this query.
80
     *
81
     * @var string
82
     */
83
    private $sql;
84
85
    /**
86
     * The query parameters.
87
     *
88
     * @var mixed[]
89
     */
90
    private $params = [];
91
92
    /**
93
     * The parameter type map of this query.
94
     *
95
     * @var int[]|string[]
96
     */
97
    private $paramTypes = [];
98
99
    /**
100
     * The type of query this is. Can be select, update or delete.
101
     *
102
     * @var int
103
     */
104
    private $type = self::SELECT;
105
106
    /**
107
     * The state of the query object. Can be dirty or clean.
108
     *
109
     * @var int
110
     */
111
    private $state = self::STATE_CLEAN;
112
113
    /**
114
     * The index of the first result to retrieve.
115
     *
116
     * @var int
117
     */
118
    private $firstResult = null;
119
120
    /**
121
     * The maximum number of results to retrieve or NULL to retrieve all results.
122
     *
123
     * @var int|null
124
     */
125
    private $maxResults = null;
126
127
    /**
128
     * The counter of bound parameters used with {@see bindValue).
129
     *
130
     * @var int
131
     */
132
    private $boundCounter = 0;
133
134
    /**
135
     * Initializes a new <tt>QueryBuilder</tt>.
136
     *
137
     * @param Connection $connection The DBAL Connection.
138
     */
139 1850
    public function __construct(Connection $connection)
140
    {
141 1850
        $this->connection = $connection;
142 1850
    }
143
144
    /**
145
     * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
146
     * This producer method is intended for convenient inline usage. Example:
147
     *
148
     * <code>
149
     *     $qb = $conn->createQueryBuilder()
150
     *         ->select('u')
151
     *         ->from('users', 'u')
152
     *         ->where($qb->expr()->eq('u.id', 1));
153
     * </code>
154
     *
155
     * For more complex expression construction, consider storing the expression
156
     * builder object in a local variable.
157
     *
158
     * @return ExpressionBuilder
159
     */
160 1615
    public function expr()
161
    {
162 1615
        return $this->connection->getExpressionBuilder();
163
    }
164
165
    /**
166
     * Gets the type of the currently built query.
167
     *
168
     * @return int
169
     */
170 1023
    public function getType()
171
    {
172 1023
        return $this->type;
173
    }
174
175
    /**
176
     * Gets the associated DBAL Connection for this query builder.
177
     *
178
     * @return Connection
179
     */
180 554
    public function getConnection()
181
    {
182 554
        return $this->connection;
183
    }
184
185
    /**
186
     * Gets the state of this query builder instance.
187
     *
188
     * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
189
     */
190 539
    public function getState()
191
    {
192 539
        return $this->state;
193
    }
194
195
    /**
196
     * Executes this query using the bound parameters and their types.
197
     *
198
     * Uses {@see Connection::executeQuery} for select statements and {@see Connection::executeUpdate}
199
     * for insert, update and delete statements.
200
     *
201
     * @return Statement|int
202
     */
203
    public function execute()
204
    {
205
        if ($this->type === self::SELECT) {
206
            return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes);
207
        }
208
209
        return $this->connection->executeUpdate($this->getSQL(), $this->params, $this->paramTypes);
210
    }
211
212
    /**
213
     * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
214
     *
215
     * <code>
216
     *     $qb = $em->createQueryBuilder()
217
     *         ->select('u')
218
     *         ->from('User', 'u')
219
     *     echo $qb->getSQL(); // SELECT u FROM User u
220
     * </code>
221
     *
222
     * @return string The SQL query string.
223
     */
224 1808
    public function getSQL()
225
    {
226 1808
        if ($this->sql !== null && $this->state === self::STATE_CLEAN) {
227 529
            return $this->sql;
228
        }
229
230 1808
        switch ($this->type) {
231 1808
            case self::INSERT:
232 691
                $sql = $this->getSQLForInsert();
233 691
                break;
234 1792
            case self::DELETE:
235 787
                $sql = $this->getSQLForDelete();
236 787
                break;
237
238 1780
            case self::UPDATE:
239 887
                $sql = $this->getSQLForUpdate();
240 887
                break;
241
242 1768
            case self::SELECT:
243
            default:
244 1768
                $sql = $this->getSQLForSelect();
245 1760
                break;
246
        }
247
248 1800
        $this->state = self::STATE_CLEAN;
249 1800
        $this->sql   = $sql;
250
251 1800
        return $sql;
252
    }
253
254
    /**
255
     * Sets a query parameter for the query being constructed.
256
     *
257
     * <code>
258
     *     $qb = $conn->createQueryBuilder()
259
     *         ->select('u')
260
     *         ->from('users', 'u')
261
     *         ->where('u.id = :user_id')
262
     *         ->setParameter(':user_id', 1);
263
     * </code>
264
     *
265
     * @param string|int      $key   The parameter position or name.
266
     * @param mixed           $value The parameter value.
267
     * @param string|int|null $type  One of the {@link \Doctrine\DBAL\ParameterType} constants.
268
     *
269
     * @return $this This QueryBuilder instance.
270
     */
271 449
    public function setParameter($key, $value, $type = null)
272
    {
273 449
        if ($type !== null) {
274 445
            $this->paramTypes[$key] = $type;
275
        }
276
277 449
        $this->params[$key] = $value;
278
279 449
        return $this;
280
    }
281
282
    /**
283
     * Sets a collection of query parameters for the query being constructed.
284
     *
285
     * <code>
286
     *     $qb = $conn->createQueryBuilder()
287
     *         ->select('u')
288
     *         ->from('users', 'u')
289
     *         ->where('u.id = :user_id1 OR u.id = :user_id2')
290
     *         ->setParameters(array(
291
     *             ':user_id1' => 1,
292
     *             ':user_id2' => 2
293
     *         ));
294
     * </code>
295
     *
296
     * @param mixed[]        $params The query parameters to set.
297
     * @param int[]|string[] $types  The query parameters types to set.
298
     *
299
     * @return $this This QueryBuilder instance.
300
     */
301
    public function setParameters(array $params, array $types = [])
302
    {
303
        $this->paramTypes = $types;
304
        $this->params     = $params;
305
306
        return $this;
307
    }
308
309
    /**
310
     * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
311
     *
312
     * @return mixed[] The currently defined query parameters indexed by parameter index or name.
313
     */
314 229
    public function getParameters()
315
    {
316 229
        return $this->params;
317
    }
318
319
    /**
320
     * Gets a (previously set) query parameter of the query being constructed.
321
     *
322
     * @param mixed $key The key (index or name) of the bound parameter.
323
     *
324
     * @return mixed The value of the bound parameter.
325
     */
326 437
    public function getParameter($key)
327
    {
328 437
        return $this->params[$key] ?? null;
329
    }
330
331
    /**
332
     * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
333
     *
334
     * @return int[]|string[] The currently defined query parameter types indexed by parameter index or name.
335
     */
336 54
    public function getParameterTypes()
337
    {
338 54
        return $this->paramTypes;
339
    }
340
341
    /**
342
     * Gets a (previously set) query parameter type of the query being constructed.
343
     *
344
     * @param mixed $key The key (index or name) of the bound parameter type.
345
     *
346
     * @return mixed The value of the bound parameter type.
347
     */
348 441
    public function getParameterType($key)
349
    {
350 441
        return $this->paramTypes[$key] ?? null;
351
    }
352
353
    /**
354
     * Sets the position of the first result to retrieve (the "offset").
355
     *
356
     * @param int $firstResult The first result to return.
357
     *
358
     * @return $this This QueryBuilder instance.
359
     */
360 4
    public function setFirstResult($firstResult)
361
    {
362 4
        $this->state       = self::STATE_DIRTY;
363 4
        $this->firstResult = $firstResult;
364
365 4
        return $this;
366
    }
367
368
    /**
369
     * Gets the position of the first result the query object was set to retrieve (the "offset").
370
     *
371
     * @return int The position of the first result.
372
     */
373 2
    public function getFirstResult()
374 2
    {
375 2
        return $this->firstResult;
376 2
    }
377
378
    /**
379
     * Sets the maximum number of results to retrieve (the "limit").
380
     *
381
     * @param int|null $maxResults The maximum number of results to retrieve or NULL to retrieve all results.
382
     *
383
     * @return $this This QueryBuilder instance.
384
     */
385 64
    public function setMaxResults($maxResults)
386 442
    {
387 64
        $this->state      = self::STATE_DIRTY;
388 506
        $this->maxResults = $maxResults;
389 442
390 64
        return $this;
391 442
    }
392
393
    /**
394
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
395
     * Returns NULL if all results will be returned.
396
     *
397
     * @return int The maximum number of results.
398
     */
399 64
    public function getMaxResults()
400 442
    {
401 64
        return $this->maxResults;
402 442
    }
403
404
    /**
405
     * Either appends to or replaces a single, generic query part.
406
     *
407
     * The available parts are: 'select', 'from', 'set', 'where',
408
     * 'groupBy', 'having' and 'orderBy'.
409
     *
410
     * @param string $sqlPartName
411
     * @param mixed  $sqlPart
412
     * @param bool   $append
413
     *
414
     * @return $this This QueryBuilder instance.
415
     */
416 312
    public function add($sqlPartName, $sqlPart, $append = false)
417 1504
    {
418 312
        $isArray    = is_array($sqlPart);
419 1816
        $isMultiple = is_array($this->sqlParts[$sqlPartName]);
420 1504
421 312
        if ($isMultiple && ! $isArray) {
422 1644
            $sqlPart = [$sqlPart];
423 938
        }
424
425 312
        $this->state = self::STATE_DIRTY;
426 1504
427 312
        if ($append) {
428 1797
            if ($sqlPartName === 'orderBy' || $sqlPartName === 'groupBy' || $sqlPartName === 'select' || $sqlPartName === 'set') {
429 1634
                foreach ($sqlPart as $part) {
430 1286
                    $this->sqlParts[$sqlPartName][] = $part;
431 1118
                }
432 287
            } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) {
433 1666
                $key                                  = key($sqlPart);
434 1528
                $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key];
435 1609
            } elseif ($isMultiple) {
436 1747
                $this->sqlParts[$sqlPartName][] = $sqlPart;
437 1460
            } else {
438 184
                $this->sqlParts[$sqlPartName] = $sqlPart;
439 1248
            }
440
441 293
            return $this;
442 1466
        }
443
444 312
        $this->sqlParts[$sqlPartName] = $sqlPart;
445 1504
446 312
        return $this;
447 1504
    }
448
449
    /**
450
     * Specifies an item that is to be returned in the query result.
451
     * Replaces any previously specified selections, if any.
452
     *
453
     * <code>
454
     *     $qb = $conn->createQueryBuilder()
455
     *         ->select('u.id', 'p.id')
456
     *         ->from('users', 'u')
457
     *         ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
458
     * </code>
459
     *
460
     * @param mixed $select The selection expressions.
461
     *
462
     * @return $this This QueryBuilder instance.
463
     */
464 294
    public function select($select = null)
465 1486
    {
466 294
        $this->type = self::SELECT;
467 1486
468 294
        if (empty($select)) {
469 1605
            return $this;
470 860
        }
471
472 292
        $selects = is_array($select) ? $select : func_get_args();
473 1484
474 292
        return $this->add('select', $selects);
475 1484
    }
476
477
    /**
478
     * Adds DISTINCT to the query.
479
     *
480
     * <code>
481
     *     $qb = $conn->createQueryBuilder()
482
     *         ->select('u.id')
483
     *         ->distinct()
484
     *         ->from('users', 'u')
485
     * </code>
486
     *
487
     * @return $this This QueryBuilder instance.
488
     */
489 188
    public function distinct() : self
490 1344
    {
491 188
        $this->sqlParts['distinct'] = true;
492 1344
493 188
        return $this;
494 1344
    }
495
496
    /**
497
     * Adds an item that is to be returned in the query result.
498
     *
499
     * <code>
500
     *     $qb = $conn->createQueryBuilder()
501
     *         ->select('u.id')
502
     *         ->addSelect('p.id')
503
     *         ->from('users', 'u')
504
     *         ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
505
     * </code>
506
     *
507
     * @param mixed $select The selection expression.
508
     *
509
     * @return $this This QueryBuilder instance.
510
     */
511 120
    public function addSelect($select = null)
512 842
    {
513 120
        $this->type = self::SELECT;
514 842
515 120
        if (empty($select)) {
516 955
            return $this;
517 816
        }
518
519 118
        $selects = is_array($select) ? $select : func_get_args();
520 840
521 118
        return $this->add('select', $selects, true);
522 840
    }
523
524
    /**
525
     * Turns the query being built into a bulk delete query that ranges over
526
     * a certain table.
527
     *
528
     * <code>
529
     *     $qb = $conn->createQueryBuilder()
530
     *         ->delete('users', 'u')
531
     *         ->where('u.id = :user_id')
532
     *         ->setParameter(':user_id', 1);
533
     * </code>
534
     *
535
     * @param string $delete The table whose rows are subject to the deletion.
536
     * @param string $alias  The table alias used in the constructed query.
537
     *
538
     * @return $this This QueryBuilder instance.
539
     */
540 101
    public function delete($delete = null, $alias = null)
541 690
    {
542 101
        $this->type = self::DELETE;
543 690
544 101
        if (! $delete) {
545 776
            return $this;
546 618
        }
547
548 99
        return $this->add('from', [
549 787
            'table' => $delete,
550 787
            'alias' => $alias,
551 688
        ]);
552
    }
553
554
    /**
555
     * Turns the query being built into a bulk update query that ranges over
556
     * a certain table
557
     *
558
     * <code>
559
     *     $qb = $conn->createQueryBuilder()
560
     *         ->update('counters', 'c')
561
     *         ->set('c.value', 'c.value + 1')
562
     *         ->where('c.id = ?');
563
     * </code>
564
     *
565
     * @param string $update The table whose rows are subject to the update.
566
     * @param string $alias  The table alias used in the constructed query.
567
     *
568
     * @return $this This QueryBuilder instance.
569
     */
570 113
    public function update($update = null, $alias = null)
571 778
    {
572 113
        $this->type = self::UPDATE;
573 778
574 113
        if (! $update) {
575 876
            return $this;
576 706
        }
577
578 111
        return $this->add('from', [
579 887
            'table' => $update,
580 887
            'alias' => $alias,
581 776
        ]);
582
    }
583
584
    /**
585
     * Turns the query being built into an insert query that inserts into
586
     * a certain table
587
     *
588
     * <code>
589
     *     $qb = $conn->createQueryBuilder()
590
     *         ->insert('users')
591
     *         ->values(
592
     *             array(
593
     *                 'name' => '?',
594
     *                 'password' => '?'
595
     *             )
596
     *         );
597
     * </code>
598
     *
599
     * @param string $insert The table into which the rows should be inserted.
600
     *
601
     * @return $this This QueryBuilder instance.
602
     */
603 91
    public function insert($insert = null)
604 604
    {
605 91
        $this->type = self::INSERT;
606 604
607 91
        if (! $insert) {
608 675
            return $this;
609 508
        }
610
611 89
        return $this->add('from', ['table' => $insert]);
612 602
    }
613
614
    /**
615
     * Creates and adds a query root corresponding to the table identified by the
616
     * given alias, forming a cartesian product with any existing query roots.
617
     *
618
     * <code>
619
     *     $qb = $conn->createQueryBuilder()
620
     *         ->select('u.id')
621
     *         ->from('users', 'u')
622
     * </code>
623
     *
624
     * @param string      $from  The table.
625
     * @param string|null $alias The alias of the table.
626
     *
627
     * @return $this This QueryBuilder instance.
628
     */
629 287
    public function from($from, $alias = null)
630 1460
    {
631 287
        return $this->add('from', [
632 1747
            'table' => $from,
633 1747
            'alias' => $alias,
634 1747
        ], true);
635 1460
    }
636
637
    /**
638
     * Creates and adds a join to the query.
639
     *
640
     * <code>
641
     *     $qb = $conn->createQueryBuilder()
642
     *         ->select('u.name')
643
     *         ->from('users', 'u')
644
     *         ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
645
     * </code>
646
     *
647
     * @param string $fromAlias The alias that points to a from clause.
648
     * @param string $join      The table name to join.
649
     * @param string $alias     The alias of the join table.
650
     * @param string $condition The condition for the join.
651
     *
652
     * @return $this This QueryBuilder instance.
653
     */
654 187
    public function join($fromAlias, $join, $alias, $condition = null)
655 1284
    {
656 187
        return $this->innerJoin($fromAlias, $join, $alias, $condition);
657 1284
    }
658
659
    /**
660
     * Creates and adds a join to the query.
661
     *
662
     * <code>
663
     *     $qb = $conn->createQueryBuilder()
664
     *         ->select('u.name')
665
     *         ->from('users', 'u')
666
     *         ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
667
     * </code>
668
     *
669
     * @param string $fromAlias The alias that points to a from clause.
670
     * @param string $join      The table name to join.
671
     * @param string $alias     The alias of the join table.
672
     * @param string $condition The condition for the join.
673
     *
674
     * @return $this This QueryBuilder instance.
675
     */
676 199
    public function innerJoin($fromAlias, $join, $alias, $condition = null)
677 1296
    {
678 199
        return $this->add('join', [
679 1296
            $fromAlias => [
680 199
                'joinType'      => 'inner',
681 1495
                'joinTable'     => $join,
682 1495
                'joinAlias'     => $alias,
683 1495
                'joinCondition' => $condition,
684 1296
            ],
685 199
        ], true);
686 1296
    }
687
688
    /**
689
     * Creates and adds a left join to the query.
690
     *
691
     * <code>
692
     *     $qb = $conn->createQueryBuilder()
693
     *         ->select('u.name')
694
     *         ->from('users', 'u')
695
     *         ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
696
     * </code>
697
     *
698
     * @param string $fromAlias The alias that points to a from clause.
699
     * @param string $join      The table name to join.
700
     * @param string $alias     The alias of the join table.
701
     * @param string $condition The condition for the join.
702
     *
703
     * @return $this This QueryBuilder instance.
704
     */
705 182
    public function leftJoin($fromAlias, $join, $alias, $condition = null)
706 1300
    {
707 182
        return $this->add('join', [
708 1300
            $fromAlias => [
709 182
                'joinType'      => 'left',
710 1482
                'joinTable'     => $join,
711 1482
                'joinAlias'     => $alias,
712 1482
                'joinCondition' => $condition,
713 1300
            ],
714 182
        ], true);
715 1300
    }
716
717
    /**
718
     * Creates and adds a right join to the query.
719
     *
720
     * <code>
721
     *     $qb = $conn->createQueryBuilder()
722
     *         ->select('u.name')
723
     *         ->from('users', 'u')
724
     *         ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
725
     * </code>
726
     *
727
     * @param string $fromAlias The alias that points to a from clause.
728
     * @param string $join      The table name to join.
729
     * @param string $alias     The alias of the join table.
730
     * @param string $condition The condition for the join.
731
     *
732
     * @return $this This QueryBuilder instance.
733
     */
734 2
    public function rightJoin($fromAlias, $join, $alias, $condition = null)
735 2
    {
736 2
        return $this->add('join', [
737 2
            $fromAlias => [
738 2
                'joinType'      => 'right',
739 4
                'joinTable'     => $join,
740 4
                'joinAlias'     => $alias,
741 4
                'joinCondition' => $condition,
742 2
            ],
743 2
        ], true);
744 2
    }
745
746
    /**
747
     * Sets a new value for a column in a bulk update query.
748
     *
749
     * <code>
750
     *     $qb = $conn->createQueryBuilder()
751
     *         ->update('counters', 'c')
752
     *         ->set('c.value', 'c.value + 1')
753
     *         ->where('c.id = ?');
754
     * </code>
755
     *
756
     * @param string $key   The column to set.
757
     * @param string $value The value, expression, placeholder, etc.
758
     *
759
     * @return $this This QueryBuilder instance.
760
     */
761 111
    public function set($key, $value)
762 776
    {
763 111
        return $this->add('set', $key . ' = ' . $value, true);
764 776
    }
765
766
    /**
767
     * Specifies one or more restrictions to the query result.
768
     * Replaces any previously specified restrictions, if any.
769
     *
770
     * <code>
771
     *     $qb = $conn->createQueryBuilder()
772
     *         ->select('c.value')
773
     *         ->from('counters', 'c')
774
     *         ->where('c.id = ?');
775
     *
776
     *     // You can optionally programatically build and/or expressions
777
     *     $qb = $conn->createQueryBuilder();
778
     *
779
     *     $or = $qb->expr()->orx();
780
     *     $or->add($qb->expr()->eq('c.id', 1));
781
     *     $or->add($qb->expr()->eq('c.id', 2));
782
     *
783
     *     $qb->update('counters', 'c')
784
     *         ->set('c.value', 'c.value + 1')
785
     *         ->where($or);
786
     * </code>
787
     *
788
     * @param mixed $predicates The restriction predicates.
789
     *
790
     * @return $this This QueryBuilder instance.
791
     */
792 221
    public function where($predicates)
793 1358
    {
794 221
        if (! (func_num_args() === 1 && $predicates instanceof CompositeExpression)) {
795 1562
            $predicates = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
796 1268
        }
797
798 221
        return $this->add('where', $predicates);
799 1358
    }
800
801
    /**
802
     * Adds one or more restrictions to the query results, forming a logical
803
     * conjunction with any previously specified restrictions.
804
     *
805
     * <code>
806
     *     $qb = $conn->createQueryBuilder()
807
     *         ->select('u')
808
     *         ->from('users', 'u')
809
     *         ->where('u.username LIKE ?')
810
     *         ->andWhere('u.is_active = 1');
811
     * </code>
812
     *
813
     * @see where()
814
     *
815
     * @param mixed $where The query restrictions.
816
     *
817
     * @return $this This QueryBuilder instance.
818
     */
819 180
    public function andWhere($where)
820 1244
    {
821 180
        $args  = func_get_args();
822 1424
        $where = $this->getQueryPart('where');
823 1244
824 180
        if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
825 1424
            $where->addMultiple($args);
826 1244
        } else {
827 161
            array_unshift($args, $where);
828 1329
            $where = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
829 1168
        }
830
831 180
        return $this->add('where', $where, true);
832 1244
    }
833
834
    /**
835
     * Adds one or more restrictions to the query results, forming a logical
836
     * disjunction with any previously specified restrictions.
837
     *
838
     * <code>
839
     *     $qb = $em->createQueryBuilder()
840
     *         ->select('u.name')
841
     *         ->from('users', 'u')
842
     *         ->where('u.id = 1')
843
     *         ->orWhere('u.id = 2');
844
     * </code>
845
     *
846
     * @see where()
847
     *
848
     * @param mixed $where The WHERE statement.
849
     *
850
     * @return $this This QueryBuilder instance.
851
     */
852 171
    public function orWhere($where)
853 1216
    {
854 171
        $args  = func_get_args();
855 1387
        $where = $this->getQueryPart('where');
856 1216
857 171
        if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
858 1380
            $where->addMultiple($args);
859 1190
        } else {
860 171
            array_unshift($args, $where);
861 1387
            $where = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
862 1216
        }
863
864 171
        return $this->add('where', $where, true);
865 1216
    }
866
867
    /**
868
     * Specifies a grouping over the results of the query.
869
     * Replaces any previously specified groupings, if any.
870
     *
871
     * <code>
872
     *     $qb = $conn->createQueryBuilder()
873
     *         ->select('u.name')
874
     *         ->from('users', 'u')
875
     *         ->groupBy('u.id');
876
     * </code>
877
     *
878
     * @param mixed $groupBy The grouping expression.
879
     *
880
     * @return $this This QueryBuilder instance.
881
     */
882 176
    public function groupBy($groupBy)
883 1164
    {
884 176
        if (empty($groupBy)) {
885 1319
            return $this;
886 1124
        }
887
888 174
        $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
889 1162
890 174
        return $this->add('groupBy', $groupBy, false);
891 1162
    }
892
893
    /**
894
     * Adds a grouping expression to the query.
895
     *
896
     * <code>
897
     *     $qb = $conn->createQueryBuilder()
898
     *         ->select('u.name')
899
     *         ->from('users', 'u')
900
     *         ->groupBy('u.lastLogin')
901
     *         ->addGroupBy('u.createdAt');
902
     * </code>
903
     *
904
     * @param mixed $groupBy The grouping expression.
905
     *
906
     * @return $this This QueryBuilder instance.
907
     */
908 156
    public function addGroupBy($groupBy)
909 1106
    {
910 156
        if (empty($groupBy)) {
911 1108
            return $this;
912 2
        }
913
914 154
        $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
915 1104
916 154
        return $this->add('groupBy', $groupBy, true);
917 1104
    }
918
919
    /**
920
     * Sets a value for a column in an insert query.
921
     *
922
     * <code>
923
     *     $qb = $conn->createQueryBuilder()
924
     *         ->insert('users')
925
     *         ->values(
926
     *             array(
927
     *                 'name' => '?'
928
     *             )
929
     *         )
930
     *         ->setValue('password', '?');
931
     * </code>
932
     *
933
     * @param string $column The column into which the value should be inserted.
934
     * @param string $value  The value that should be inserted into the column.
935
     *
936
     * @return $this This QueryBuilder instance.
937
     */
938 79
    public function setValue($column, $value)
939 554
    {
940 79
        $this->sqlParts['values'][$column] = $value;
941 554
942 79
        return $this;
943 554
    }
944
945
    /**
946
     * Specifies values for an insert query indexed by column names.
947
     * Replaces any previous values, if any.
948
     *
949
     * <code>
950
     *     $qb = $conn->createQueryBuilder()
951
     *         ->insert('users')
952
     *         ->values(
953
     *             array(
954
     *                 'name' => '?',
955
     *                 'password' => '?'
956
     *             )
957
     *         );
958
     * </code>
959
     *
960
     * @param mixed[] $values The values to specify for the insert query indexed by column names.
961
     *
962
     * @return $this This QueryBuilder instance.
963
     */
964 87
    public function values(array $values)
965 600
    {
966 87
        return $this->add('values', $values);
967 600
    }
968
969
    /**
970
     * Specifies a restriction over the groups of the query.
971
     * Replaces any previous having restrictions, if any.
972
     *
973
     * @param mixed $having The restriction over the groups.
974
     *
975
     * @return $this This QueryBuilder instance.
976
     */
977 152
    public function having($having)
978 1064
    {
979 152
        if (! (func_num_args() === 1 && $having instanceof CompositeExpression)) {
980 1216
            $having = new CompositeExpression(CompositeExpression::TYPE_AND, func_get_args());
981 1064
        }
982
983 152
        return $this->add('having', $having);
984 1064
    }
985
986
    /**
987
     * Adds a restriction over the groups of the query, forming a logical
988
     * conjunction with any existing having restrictions.
989
     *
990
     * @param mixed $having The restriction to append.
991
     *
992
     * @return $this This QueryBuilder instance.
993
     */
994 147
    public function andHaving($having)
995 1040
    {
996 147
        $args   = func_get_args();
997 1187
        $having = $this->getQueryPart('having');
998 1040
999 147
        if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
1000 1180
            $having->addMultiple($args);
1001 1014
        } else {
1002 145
            array_unshift($args, $having);
1003 1183
            $having = new CompositeExpression(CompositeExpression::TYPE_AND, $args);
1004 1038
        }
1005
1006 147
        return $this->add('having', $having);
1007 1040
    }
1008
1009
    /**
1010
     * Adds a restriction over the groups of the query, forming a logical
1011
     * disjunction with any existing having restrictions.
1012
     *
1013
     * @param mixed $having The restriction to add.
1014
     *
1015
     * @return $this This QueryBuilder instance.
1016
     */
1017 141
    public function orHaving($having)
1018 996
    {
1019 141
        $args   = func_get_args();
1020 1137
        $having = $this->getQueryPart('having');
1021 996
1022 141
        if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
1023 1130
            $having->addMultiple($args);
1024 970
        } else {
1025 141
            array_unshift($args, $having);
1026 1137
            $having = new CompositeExpression(CompositeExpression::TYPE_OR, $args);
1027 996
        }
1028
1029 141
        return $this->add('having', $having);
1030 996
    }
1031
1032
    /**
1033
     * Specifies an ordering for the query results.
1034
     * Replaces any previously specified orderings, if any.
1035
     *
1036
     * @param string $sort  The ordering expression.
1037
     * @param string $order The ordering direction.
1038
     *
1039
     * @return $this This QueryBuilder instance.
1040
     */
1041 132
    public function orderBy($sort, $order = null)
1042 930
    {
1043 132
        return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false);
1044 930
    }
1045
1046
    /**
1047
     * Adds an ordering to the query results.
1048
     *
1049
     * @param string $sort  The ordering expression.
1050
     * @param string $order The ordering direction.
1051
     *
1052
     * @return $this This QueryBuilder instance.
1053
     */
1054 127
    public function addOrderBy($sort, $order = null)
1055 906
    {
1056 127
        return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true);
1057 906
    }
1058
1059
    /**
1060
     * Gets a query part by its name.
1061
     *
1062
     * @param string $queryPartName
1063
     *
1064
     * @return mixed
1065
     */
1066 194
    public function getQueryPart($queryPartName)
1067 1258
    {
1068 194
        return $this->sqlParts[$queryPartName];
1069 1258
    }
1070
1071
    /**
1072
     * Gets all query parts.
1073
     *
1074
     * @return mixed[]
1075
     */
1076 29
    public function getQueryParts()
1077 200
    {
1078 29
        return $this->sqlParts;
1079 200
    }
1080
1081
    /**
1082
     * Resets SQL parts.
1083
     *
1084
     * @param string[]|null $queryPartNames
1085
     *
1086
     * @return $this This QueryBuilder instance.
1087
     */
1088 56
    public function resetQueryParts($queryPartNames = null)
1089 398
    {
1090 56
        if ($queryPartNames === null) {
1091 398
            $queryPartNames = array_keys($this->sqlParts);
1092
        }
1093
1094 56
        foreach ($queryPartNames as $queryPartName) {
1095 454
            $this->resetQueryPart($queryPartName);
1096 398
        }
1097
1098 56
        return $this;
1099 398
    }
1100
1101
    /**
1102
     * Resets a single SQL part.
1103
     *
1104
     * @param string $queryPartName
1105
     *
1106
     * @return $this This QueryBuilder instance.
1107
     */
1108 61
    public function resetQueryPart($queryPartName)
1109 422
    {
1110 61
        $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName];
1111 422
1112 61
        $this->state = self::STATE_DIRTY;
1113 422
1114 61
        return $this;
1115 422
    }
1116
1117
    /**
1118
     * @return string
1119
     *
1120
     * @throws QueryException
1121
     */
1122 288
    private function getSQLForSelect()
1123 1480
    {
1124 288
        $query = 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' : '') .
1125 1768
                  implode(', ', $this->sqlParts['select']);
1126 1480
1127 288
        $query .= ($this->sqlParts['from'] ? ' FROM ' . implode(', ', $this->getFromClauses()) : '')
1128 1764
            . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '')
1129 1760
            . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '')
1130 1760
            . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '')
1131 1760
            . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : '');
1132 1476
1133 284
        if ($this->isLimitQuery()) {
1134 1476
            return $this->connection->getDatabasePlatform()->modifyLimitQuery(
1135
                $query,
1136
                $this->maxResults,
1137
                $this->firstResult
1138
            );
1139
        }
1140
1141 284
        return $query;
1142 1476
    }
1143
1144
    /**
1145
     * @return string[]
1146
     */
1147 283
    private function getFromClauses()
1148 1456
    {
1149 283
        $fromClauses  = [];
1150 1739
        $knownAliases = [];
1151 1456
1152
        // Loop through all FROM clauses
1153 283
        foreach ($this->sqlParts['from'] as $from) {
1154 1739
            if ($from['alias'] === null) {
1155 1492
                $tableSql       = $from['table'];
1156 224
                $tableReference = $from['table'];
1157 188
            } else {
1158 273
                $tableSql       = $from['table'] . ' ' . $from['alias'];
1159 1719
                $tableReference = $from['alias'];
1160 1446
            }
1161
1162 283
            $knownAliases[$tableReference] = true;
1163 1456
1164 283
            $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases);
1165 1456
        }
1166
1167 281
        $this->verifyAllAliasesAreKnown($knownAliases);
1168 1454
1169 279
        return $fromClauses;
1170 1452
    }
1171
1172
    /**
1173
     * @param string[] $knownAliases
1174
     *
1175
     * @throws QueryException
1176
     */
1177 281
    private function verifyAllAliasesAreKnown(array $knownAliases)
1178 1454
    {
1179 281
        foreach ($this->sqlParts['join'] as $fromAlias => $joins) {
1180 1658
            if (! isset($knownAliases[$fromAlias])) {
1181 1364
                throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases));
1182 310
            }
1183
        }
1184 279
    }
1185 1452
1186
    /**
1187
     * @return bool
1188
     */
1189 284
    private function isLimitQuery()
1190 1476
    {
1191 284
        return $this->maxResults !== null || $this->firstResult !== null;
1192 1476
    }
1193
1194
    /**
1195
     * Converts this instance into an INSERT string in SQL.
1196
     *
1197
     * @return string
1198
     */
1199 89
    private function getSQLForInsert()
1200 602
    {
1201 89
        return 'INSERT INTO ' . $this->sqlParts['from']['table'] .
1202 691
        ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' .
1203 691
        ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')';
1204 602
    }
1205
1206
    /**
1207
     * Converts this instance into an UPDATE string in SQL.
1208
     *
1209
     * @return string
1210
     */
1211 111
    private function getSQLForUpdate()
1212 776
    {
1213 111
        $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
1214 776
1215 111
        return 'UPDATE ' . $table
1216 887
            . ' SET ' . implode(', ', $this->sqlParts['set'])
1217 887
            . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
1218 776
    }
1219
1220
    /**
1221
     * Converts this instance into a DELETE string in SQL.
1222
     *
1223
     * @return string
1224
     */
1225 99
    private function getSQLForDelete()
1226 688
    {
1227 99
        $table = $this->sqlParts['from']['table'] . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
1228 688
1229 99
        return 'DELETE FROM ' . $table . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
1230 688
    }
1231
1232
    /**
1233
     * Gets a string representation of this QueryBuilder which corresponds to
1234
     * the final SQL query being constructed.
1235
     *
1236
     * @return string The string representation of this QueryBuilder.
1237
     */
1238 294
    public function __toString()
1239 1486
    {
1240 294
        return $this->getSQL();
1241 1486
    }
1242
1243
    /**
1244
     * Creates a new named parameter and bind the value $value to it.
1245
     *
1246
     * This method provides a shortcut for PDOStatement::bindValue
1247
     * when using prepared statements.
1248
     *
1249
     * The parameter $value specifies the value that you want to bind. If
1250
     * $placeholder is not provided bindValue() will automatically create a
1251
     * placeholder for you. An automatic placeholder will be of the name
1252
     * ':dcValue1', ':dcValue2' etc.
1253
     *
1254
     * For more information see {@link http://php.net/pdostatement-bindparam}
1255
     *
1256
     * Example:
1257
     * <code>
1258
     * $value = 2;
1259
     * $q->eq( 'id', $q->bindValue( $value ) );
1260
     * $stmt = $q->executeQuery(); // executed with 'id = 2'
1261
     * </code>
1262
     *
1263
     * @link http://www.zetacomponents.org
1264
     *
1265
     * @param mixed  $value
1266
     * @param mixed  $type
1267
     * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
1268
     *
1269
     * @return string the placeholder name used.
1270
     */
1271 55
    public function createNamedParameter($value, $type = ParameterType::STRING, $placeHolder = null)
1272 378
    {
1273 55
        if ($placeHolder === null) {
1274 431
            $this->boundCounter++;
1275 429
            $placeHolder = ':dcValue' . $this->boundCounter;
1276 376
        }
1277 55
        $this->setParameter(substr($placeHolder, 1), $value, $type);
1278 378
1279 55
        return $placeHolder;
1280 378
    }
1281
1282
    /**
1283
     * Creates a new positional parameter and bind the given value to it.
1284
     *
1285
     * Attention: If you are using positional parameters with the query builder you have
1286
     * to be very careful to bind all parameters in the order they appear in the SQL
1287
     * statement , otherwise they get bound in the wrong order which can lead to serious
1288
     * bugs in your code.
1289
     *
1290
     * Example:
1291
     * <code>
1292
     *  $qb = $conn->createQueryBuilder();
1293
     *  $qb->select('u.*')
1294
     *     ->from('users', 'u')
1295
     *     ->where('u.username = ' . $qb->createPositionalParameter('Foo', ParameterType::STRING))
1296
     *     ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', ParameterType::STRING))
1297
     * </code>
1298
     *
1299
     * @param mixed $value
1300
     * @param int   $type
1301
     *
1302
     * @return string
1303
     */
1304 47
    public function createPositionalParameter($value, $type = ParameterType::STRING)
1305 332
    {
1306 47
        $this->boundCounter++;
1307 379
        $this->setParameter($this->boundCounter, $value, $type);
1308 332
1309 47
        return '?';
1310 332
    }
1311
1312
    /**
1313
     * @param string   $fromAlias
1314
     * @param string[] $knownAliases
1315
     *
1316
     * @return string
1317
     *
1318
     * @throws QueryException
1319
     */
1320 283
    private function getSQLForJoins($fromAlias, array &$knownAliases)
1321 1456
    {
1322 283
        $sql = '';
1323 1456
1324 283
        if (isset($this->sqlParts['join'][$fromAlias])) {
1325 1662
            foreach ($this->sqlParts['join'][$fromAlias] as $join) {
1326 1528
                if (array_key_exists($join['joinAlias'], $knownAliases)) {
1327 1327
                    throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
1328 24
                }
1329 204
                $sql .= ' ' . strtoupper($join['joinType'])
1330 1524
                    . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'];
1331 1524
                if ($join['joinCondition'] !== null) {
1332 1522
                    $sql .= ' ON ' . $join['joinCondition'];
1333 1320
                }
1334 204
                $knownAliases[$join['joinAlias']] = true;
1335
            }
1336 1320
1337 1524
            foreach ($this->sqlParts['join'][$fromAlias] as $join) {
1338 204
                $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases);
1339
            }
1340
        }
1341 1454
1342 281
        return $sql;
1343
    }
1344
1345
    /**
1346
     * Deep clone of all expression objects in the SQL parts.
1347
     *
1348
     * @return void
1349 200
     */
1350 29
    public function __clone()
1351 200
    {
1352 229
        foreach ($this->sqlParts as $part => $elements) {
1353 229
            if (is_array($this->sqlParts[$part])) {
1354 229
                foreach ($this->sqlParts[$part] as $idx => $element) {
1355 229
                    if (! is_object($element)) {
1356 29
                        continue;
1357
                    }
1358
1359
                    $this->sqlParts[$part][$idx] = clone $element;
1360 200
                }
1361 229
            } elseif (is_object($elements)) {
1362 29
                $this->sqlParts[$part] = clone $elements;
1363
            }
1364
        }
1365 200
1366 229
        foreach ($this->params as $name => $param) {
1367 29
            if (! is_object($param)) {
1368
                continue;
1369
            }
1370 200
1371 29
            $this->params[$name] = clone $param;
1372 200
        }
1373 29
    }
1374
}
1375