Completed
Pull Request — master (#3696)
by
unknown
13:44
created

QueryBuilder::setParameter()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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