Completed
Pull Request — master (#3685)
by
unknown
62:27
created

QueryBuilder::verifyAllAliasesAreKnown()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3

Importance

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