Failed Conditions
Push — master ( 6744b4...2b8acb )
by Marco
60:45 queued 60:36
created

lib/Doctrine/ORM/QueryBuilder.php (6 issues)

Upgrade to new PHP Analysis Engine

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

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM;
21
22
use Doctrine\Common\Collections\ArrayCollection;
23
use Doctrine\Common\Collections\Criteria;
24
25
use Doctrine\ORM\Query\Expr;
26
use Doctrine\ORM\Query\QueryExpressionVisitor;
27
28
/**
29
 * This class is responsible for building DQL query strings via an object oriented
30
 * PHP interface.
31
 *
32
 * @since 2.0
33
 * @author Guilherme Blanco <[email protected]>
34
 * @author Jonathan Wage <[email protected]>
35
 * @author Roman Borschel <[email protected]>
36
 */
37
class QueryBuilder
38
{
39
    /* The query types. */
40
    const SELECT = 0;
41
    const DELETE = 1;
42
    const UPDATE = 2;
43
44
    /* The builder states. */
45
    const STATE_DIRTY = 0;
46
    const STATE_CLEAN = 1;
47
48
    /**
49
     * The EntityManager used by this QueryBuilder.
50
     *
51
     * @var EntityManagerInterface
52
     */
53
    private $_em;
54
55
    /**
56
     * The array of DQL parts collected.
57
     *
58
     * @var array
59
     */
60
    private $_dqlParts = [
61
        'distinct' => false,
62
        'select'  => [],
63
        'from'    => [],
64
        'join'    => [],
65
        'set'     => [],
66
        'where'   => null,
67
        'groupBy' => [],
68
        'having'  => null,
69
        'orderBy' => []
70
    ];
71
72
    /**
73
     * The type of query this is. Can be select, update or delete.
74
     *
75
     * @var integer
76
     */
77
    private $_type = self::SELECT;
78
79
    /**
80
     * The state of the query object. Can be dirty or clean.
81
     *
82
     * @var integer
83
     */
84
    private $_state = self::STATE_CLEAN;
85
86
    /**
87
     * The complete DQL string for this query.
88
     *
89
     * @var string
90
     */
91
    private $_dql;
92
93
    /**
94
     * The query parameters.
95
     *
96
     * @var \Doctrine\Common\Collections\ArrayCollection
97
     */
98
    private $parameters;
99
100
    /**
101
     * The index of the first result to retrieve.
102
     *
103
     * @var integer
104
     */
105
    private $_firstResult = null;
106
107
    /**
108
     * The maximum number of results to retrieve.
109
     *
110
     * @var integer
111
     */
112
    private $_maxResults = null;
113
114
    /**
115
     * Keeps root entity alias names for join entities.
116
     *
117
     * @var array
118
     */
119
    private $joinRootAliases = [];
120
121
     /**
122
     * Whether to use second level cache, if available.
123
     *
124
     * @var boolean
125
     */
126
    protected $cacheable = false;
127
128
    /**
129
     * Second level cache region name.
130
     *
131
     * @var string|null
132
     */
133
    protected $cacheRegion;
134
135
    /**
136
     * Second level query cache mode.
137
     *
138
     * @var integer|null
139
     */
140
    protected $cacheMode;
141
142
    /**
143
     * @var integer
144
     */
145
    protected $lifetime = 0;
146
147
    /**
148
     * Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>.
149
     *
150
     * @param EntityManagerInterface $em The EntityManager to use.
151
     */
152 118
    public function __construct(EntityManagerInterface $em)
153
    {
154 118
        $this->_em = $em;
155 118
        $this->parameters = new ArrayCollection();
156 118
    }
157
158
    /**
159
     * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
160
     * This producer method is intended for convenient inline usage. Example:
161
     *
162
     * <code>
163
     *     $qb = $em->createQueryBuilder();
164
     *     $qb
165
     *         ->select('u')
166
     *         ->from('User', 'u')
167
     *         ->where($qb->expr()->eq('u.id', 1));
168
     * </code>
169
     *
170
     * For more complex expression construction, consider storing the expression
171
     * builder object in a local variable.
172
     *
173
     * @return Query\Expr
174
     */
175 11
    public function expr()
176
    {
177 11
        return $this->_em->getExpressionBuilder();
178
    }
179
180
    /**
181
     *
182
     * Enable/disable second level query (result) caching for this query.
183
     *
184
     * @param boolean $cacheable
185
     *
186
     * @return self
187
     */
188 1
    public function setCacheable($cacheable)
189
    {
190 1
        $this->cacheable = (boolean) $cacheable;
191
192 1
        return $this;
193
    }
194
195
    /**
196
     * @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise.
197
     */
198 1
    public function isCacheable()
199
    {
200 1
        return $this->cacheable;
201
    }
202
203
    /**
204
     * @param string $cacheRegion
205
     *
206
     * @return self
207
     */
208 1
    public function setCacheRegion($cacheRegion)
209
    {
210 1
        $this->cacheRegion = (string) $cacheRegion;
211
212 1
        return $this;
213
    }
214
215
    /**
216
    * Obtain the name of the second level query cache region in which query results will be stored
217
    *
218
    * @return string|null The cache region name; NULL indicates the default region.
219
    */
220 1
    public function getCacheRegion()
221
    {
222 1
        return $this->cacheRegion;
223
    }
224
225
    /**
226
     * @return integer
227
     */
228 1
    public function getLifetime()
229
    {
230 1
        return $this->lifetime;
231
    }
232
233
    /**
234
     * Sets the life-time for this query into second level cache.
235
     *
236
     * @param integer $lifetime
237
     *
238
     * @return self
239
     */
240 1
    public function setLifetime($lifetime)
241
    {
242 1
        $this->lifetime = (integer) $lifetime;
243
244 1
        return $this;
245
    }
246
247
    /**
248
     * @return integer
249
     */
250 1
    public function getCacheMode()
251
    {
252 1
        return $this->cacheMode;
253
    }
254
255
    /**
256
     * @param integer $cacheMode
257
     *
258
     * @return self
259
     */
260 1
    public function setCacheMode($cacheMode)
261
    {
262 1
        $this->cacheMode = (integer) $cacheMode;
263
264 1
        return $this;
265
    }
266
267
    /**
268
     * Gets the type of the currently built query.
269
     *
270
     * @return integer
271
     */
272 4
    public function getType()
273
    {
274 4
        return $this->_type;
275
    }
276
277
    /**
278
     * Gets the associated EntityManager for this query builder.
279
     *
280
     * @return EntityManager
281
     */
282 1
    public function getEntityManager()
283
    {
284 1
        return $this->_em;
285
    }
286
287
    /**
288
     * Gets the state of this query builder instance.
289
     *
290
     * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
291
     */
292 2
    public function getState()
293
    {
294 2
        return $this->_state;
295
    }
296
297
    /**
298
     * Gets the complete DQL string formed by the current specifications of this QueryBuilder.
299
     *
300
     * <code>
301
     *     $qb = $em->createQueryBuilder()
302
     *         ->select('u')
303
     *         ->from('User', 'u');
304
     *     echo $qb->getDql(); // SELECT u FROM User u
305
     * </code>
306
     *
307
     * @return string The DQL query string.
308
     */
309 82
    public function getDQL()
310
    {
311 82
        if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) {
312 44
            return $this->_dql;
313
        }
314
315 82
        switch ($this->_type) {
316 82
            case self::DELETE:
317 1
                $dql = $this->_getDQLForDelete();
318 1
                break;
319
320 81
            case self::UPDATE:
321 3
                $dql = $this->_getDQLForUpdate();
322 3
                break;
323
324 79
            case self::SELECT:
325
            default:
326 79
                $dql = $this->_getDQLForSelect();
327 79
                break;
328
        }
329
330 82
        $this->_state = self::STATE_CLEAN;
331 82
        $this->_dql   = $dql;
332
333 82
        return $dql;
334
    }
335
336
    /**
337
     * Constructs a Query instance from the current specifications of the builder.
338
     *
339
     * <code>
340
     *     $qb = $em->createQueryBuilder()
341
     *         ->select('u')
342
     *         ->from('User', 'u');
343
     *     $q = $qb->getQuery();
344
     *     $results = $q->execute();
345
     * </code>
346
     *
347
     * @return Query
348
     */
349 71
    public function getQuery()
350
    {
351 71
        $parameters = clone $this->parameters;
352 71
        $query      = $this->_em->createQuery($this->getDQL())
353 71
            ->setParameters($parameters)
354 71
            ->setFirstResult($this->_firstResult)
355 71
            ->setMaxResults($this->_maxResults);
356
357 71
        if ($this->lifetime) {
358 1
            $query->setLifetime($this->lifetime);
359
        }
360
361 71
        if ($this->cacheMode) {
362 1
            $query->setCacheMode($this->cacheMode);
363
        }
364
365 71
        if ($this->cacheable) {
366 1
            $query->setCacheable($this->cacheable);
367
        }
368
369 71
        if ($this->cacheRegion) {
370 1
            $query->setCacheRegion($this->cacheRegion);
371
        }
372
373 71
        return $query;
374
    }
375
376
    /**
377
     * Finds the root entity alias of the joined entity.
378
     *
379
     * @param string $alias       The alias of the new join entity
380
     * @param string $parentAlias The parent entity alias of the join relationship
381
     *
382
     * @return string
383
     */
384 29
    private function findRootAlias($alias, $parentAlias)
385
    {
386 29
        $rootAlias = null;
387
388 29
        if (in_array($parentAlias, $this->getRootAliases())) {
389 29
            $rootAlias = $parentAlias;
390 6
        } elseif (isset($this->joinRootAliases[$parentAlias])) {
391 6
            $rootAlias = $this->joinRootAliases[$parentAlias];
392
        } else {
393
            // Should never happen with correct joining order. Might be
394
            // thoughtful to throw exception instead.
395
            $rootAlias = $this->getRootAlias();
396
        }
397
398 29
        $this->joinRootAliases[$alias] = $rootAlias;
399
400 29
        return $rootAlias;
401
    }
402
403
    /**
404
     * Gets the FIRST root alias of the query. This is the first entity alias involved
405
     * in the construction of the query.
406
     *
407
     * <code>
408
     * $qb = $em->createQueryBuilder()
409
     *     ->select('u')
410
     *     ->from('User', 'u');
411
     *
412
     * echo $qb->getRootAlias(); // u
413
     * </code>
414
     *
415
     * @deprecated Please use $qb->getRootAliases() instead.
416
     * @throws \RuntimeException
417
     *
418
     * @return string
419
     */
420 3
    public function getRootAlias()
421
    {
422 3
        $aliases = $this->getRootAliases();
423
424 3
        if ( ! isset($aliases[0])) {
425
            throw new \RuntimeException('No alias was set before invoking getRootAlias().');
426
        }
427
428 3
        return $aliases[0];
429
    }
430
431
    /**
432
     * Gets the root aliases of the query. This is the entity aliases involved
433
     * in the construction of the query.
434
     *
435
     * <code>
436
     *     $qb = $em->createQueryBuilder()
437
     *         ->select('u')
438
     *         ->from('User', 'u');
439
     *
440
     *     $qb->getRootAliases(); // array('u')
441
     * </code>
442
     *
443
     * @return array
444
     */
445 45 View Code Duplication
    public function getRootAliases()
446
    {
447 45
        $aliases = [];
448
449 45
        foreach ($this->_dqlParts['from'] as &$fromClause) {
450 45
            if (is_string($fromClause)) {
451
                $spacePos = strrpos($fromClause, ' ');
452
                $from     = substr($fromClause, 0, $spacePos);
453
                $alias    = substr($fromClause, $spacePos + 1);
454
455
                $fromClause = new Query\Expr\From($from, $alias);
456
            }
457
458 45
            $aliases[] = $fromClause->getAlias();
459
        }
460
461 45
        return $aliases;
462
    }
463
464
    /**
465
     * Gets all the aliases that have been used in the query.
466
     * Including all select root aliases and join aliases
467
     *
468
     * <code>
469
     *     $qb = $em->createQueryBuilder()
470
     *         ->select('u')
471
     *         ->from('User', 'u')
472
     *         ->join('u.articles','a');
473
     *
474
     *     $qb->getAllAliases(); // array('u','a')
475
     * </code>
476
     * @return array
477
     */
478 15
    public function getAllAliases()
479
    {
480 15
        return array_merge($this->getRootAliases(), array_keys($this->joinRootAliases));
481
    }
482
483
    /**
484
     * Gets the root entities of the query. This is the entity aliases involved
485
     * in the construction of the query.
486
     *
487
     * <code>
488
     *     $qb = $em->createQueryBuilder()
489
     *         ->select('u')
490
     *         ->from('User', 'u');
491
     *
492
     *     $qb->getRootEntities(); // array('User')
493
     * </code>
494
     *
495
     * @return array
496
     */
497 1 View Code Duplication
    public function getRootEntities()
498
    {
499 1
        $entities = [];
500
501 1
        foreach ($this->_dqlParts['from'] as &$fromClause) {
502 1
            if (is_string($fromClause)) {
503
                $spacePos = strrpos($fromClause, ' ');
504
                $from     = substr($fromClause, 0, $spacePos);
505
                $alias    = substr($fromClause, $spacePos + 1);
506
507
                $fromClause = new Query\Expr\From($from, $alias);
508
            }
509
510 1
            $entities[] = $fromClause->getFrom();
511
        }
512
513 1
        return $entities;
514
    }
515
516
    /**
517
     * Sets a query parameter for the query being constructed.
518
     *
519
     * <code>
520
     *     $qb = $em->createQueryBuilder()
521
     *         ->select('u')
522
     *         ->from('User', 'u')
523
     *         ->where('u.id = :user_id')
524
     *         ->setParameter('user_id', 1);
525
     * </code>
526
     *
527
     * @param string|integer $key   The parameter position or name.
528
     * @param mixed          $value The parameter value.
529
     * @param string|integer|null    $type  PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant
530
     *
531
     * @return self
532
     */
533 10 View Code Duplication
    public function setParameter($key, $value, $type = null)
534
    {
535 10
        $filteredParameters = $this->parameters->filter(
536 10
            function ($parameter) use ($key)
537
            {
538
                /* @var Query\Parameter $parameter */
539
                // Must not be identical because of string to integer conversion
540 3
                return ($key == $parameter->getName());
541 10
            }
542
        );
543
544 10
        if (count($filteredParameters)) {
545
            /* @var Query\Parameter $parameter */
546
            $parameter = $filteredParameters->first();
547
            $parameter->setValue($value, $type);
548
549
            return $this;
550
        }
551
552 10
        $parameter = new Query\Parameter($key, $value, $type);
553
554 10
        $this->parameters->add($parameter);
555
556 10
        return $this;
557
    }
558
559
    /**
560
     * Sets a collection of query parameters for the query being constructed.
561
     *
562
     * <code>
563
     *     $qb = $em->createQueryBuilder()
564
     *         ->select('u')
565
     *         ->from('User', 'u')
566
     *         ->where('u.id = :user_id1 OR u.id = :user_id2')
567
     *         ->setParameters(new ArrayCollection(array(
568
     *             new Parameter('user_id1', 1),
569
     *             new Parameter('user_id2', 2)
570
     *        )));
571
     * </code>
572
     *
573
     * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters to set.
574
     *
575
     * @return self
576
     */
577 4 View Code Duplication
    public function setParameters($parameters)
578
    {
579
        // BC compatibility with 2.3-
580 4
        if (is_array($parameters)) {
581 1
            $parameterCollection = new ArrayCollection();
582
583 1
            foreach ($parameters as $key => $value) {
584 1
                $parameter = new Query\Parameter($key, $value);
585
586 1
                $parameterCollection->add($parameter);
587
            }
588
589 1
            $parameters = $parameterCollection;
590
        }
591
592 4
        $this->parameters = $parameters;
593
594 4
        return $this;
595
    }
596
597
    /**
598
     * Gets all defined query parameters for the query being constructed.
599
     *
600
     * @return \Doctrine\Common\Collections\ArrayCollection The currently defined query parameters.
601
     */
602 2
    public function getParameters()
603
    {
604 2
        return $this->parameters;
605
    }
606
607
    /**
608
     * Gets a (previously set) query parameter of the query being constructed.
609
     *
610
     * @param mixed $key The key (index or name) of the bound parameter.
611
     *
612
     * @return Query\Parameter|null The value of the bound parameter.
613
     */
614 12 View Code Duplication
    public function getParameter($key)
615
    {
616 12
        $filteredParameters = $this->parameters->filter(
617 12
            function ($parameter) use ($key)
618
            {
619
                /* @var Query\Parameter $parameter */
620
                // Must not be identical because of string to integer conversion
621 12
                return ($key == $parameter->getName());
622 12
            }
623
        );
624
625 12
        return count($filteredParameters) ? $filteredParameters->first() : null;
626
    }
627
628
    /**
629
     * Sets the position of the first result to retrieve (the "offset").
630
     *
631
     * @param integer $firstResult The first result to return.
632
     *
633
     * @return self
634
     */
635 2
    public function setFirstResult($firstResult)
636
    {
637 2
        $this->_firstResult = $firstResult;
638
639 2
        return $this;
640
    }
641
642
    /**
643
     * Gets the position of the first result the query object was set to retrieve (the "offset").
644
     * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
645
     *
646
     * @return integer The position of the first result.
647
     */
648 2
    public function getFirstResult()
649
    {
650 2
        return $this->_firstResult;
651
    }
652
653
    /**
654
     * Sets the maximum number of results to retrieve (the "limit").
655
     *
656
     * @param integer $maxResults The maximum number of results to retrieve.
657
     *
658
     * @return self
659
     */
660 3
    public function setMaxResults($maxResults)
661
    {
662 3
        $this->_maxResults = $maxResults;
663
664 3
        return $this;
665
    }
666
667
    /**
668
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
669
     * Returns NULL if {@link setMaxResults} was not applied to this query builder.
670
     *
671
     * @return integer Maximum number of results.
672
     */
673 2
    public function getMaxResults()
674
    {
675 2
        return $this->_maxResults;
676
    }
677
678
    /**
679
     * Either appends to or replaces a single, generic query part.
680
     *
681
     * The available parts are: 'select', 'from', 'join', 'set', 'where',
682
     * 'groupBy', 'having' and 'orderBy'.
683
     *
684
     * @param string       $dqlPartName The DQL part name.
685
     * @param object|array $dqlPart     An Expr object.
686
     * @param bool         $append      Whether to append (true) or replace (false).
687
     *
688
     * @return self
689
     */
690 114
    public function add($dqlPartName, $dqlPart, $append = false)
691
    {
692 114
        if ($append && ($dqlPartName === "where" || $dqlPartName === "having")) {
693 1
            throw new \InvalidArgumentException(
694
                "Using \$append = true does not have an effect with 'where' or 'having' ".
695 1
                "parts. See QueryBuilder#andWhere() for an example for correct usage."
696
            );
697
        }
698
699 114
        $isMultiple = is_array($this->_dqlParts[$dqlPartName])
700 114
            && !($dqlPartName == 'join' && !$append);
701
702
        // Allow adding any part retrieved from self::getDQLParts().
703 114
        if (is_array($dqlPart) && $dqlPartName != 'join') {
704 1
            $dqlPart = reset($dqlPart);
705
        }
706
707
        // This is introduced for backwards compatibility reasons.
708
        // TODO: Remove for 3.0
709 114
        if ($dqlPartName == 'join') {
710 30
            $newDqlPart = [];
711
712 30
            foreach ($dqlPart as $k => $v) {
713 30
                $k = is_numeric($k) ? $this->getRootAlias() : $k;
714
715 30
                $newDqlPart[$k] = $v;
716
            }
717
718 30
            $dqlPart = $newDqlPart;
719
        }
720
721 114
        if ($append && $isMultiple) {
722 108
            if (is_array($dqlPart)) {
723 30
                $key = key($dqlPart);
724
725 30
                $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
726
            } else {
727 108
                $this->_dqlParts[$dqlPartName][] = $dqlPart;
728
            }
729
        } else {
730 112
            $this->_dqlParts[$dqlPartName] = ($isMultiple) ? [$dqlPart] : $dqlPart;
731
        }
732
733 114
        $this->_state = self::STATE_DIRTY;
734
735 114
        return $this;
736
    }
737
738
    /**
739
     * Specifies an item that is to be returned in the query result.
740
     * Replaces any previously specified selections, if any.
741
     *
742
     * <code>
743
     *     $qb = $em->createQueryBuilder()
744
     *         ->select('u', 'p')
745
     *         ->from('User', 'u')
746
     *         ->leftJoin('u.Phonenumbers', 'p');
747
     * </code>
748
     *
749
     * @param mixed $select The selection expressions.
750
     *
751
     * @return self
752
     */
753 107 View Code Duplication
    public function select($select = null)
754
    {
755 107
        $this->_type = self::SELECT;
756
757 107
        if (empty($select)) {
758 1
            return $this;
759
        }
760
761 106
        $selects = is_array($select) ? $select : func_get_args();
762
763 106
        return $this->add('select', new Expr\Select($selects), false);
764
    }
765
766
    /**
767
     * Adds a DISTINCT flag to this query.
768
     *
769
     * <code>
770
     *     $qb = $em->createQueryBuilder()
771
     *         ->select('u')
772
     *         ->distinct()
773
     *         ->from('User', 'u');
774
     * </code>
775
     *
776
     * @param bool $flag
777
     *
778
     * @return self
779
     */
780 1
    public function distinct($flag = true)
781
    {
782 1
        $this->_dqlParts['distinct'] = (bool) $flag;
783
784 1
        return $this;
785
    }
786
787
    /**
788
     * Adds an item that is to be returned in the query result.
789
     *
790
     * <code>
791
     *     $qb = $em->createQueryBuilder()
792
     *         ->select('u')
793
     *         ->addSelect('p')
794
     *         ->from('User', 'u')
795
     *         ->leftJoin('u.Phonenumbers', 'p');
796
     * </code>
797
     *
798
     * @param mixed $select The selection expression.
799
     *
800
     * @return self
801
     */
802 1 View Code Duplication
    public function addSelect($select = null)
803
    {
804 1
        $this->_type = self::SELECT;
805
806 1
        if (empty($select)) {
807
            return $this;
808
        }
809
810 1
        $selects = is_array($select) ? $select : func_get_args();
811
812 1
        return $this->add('select', new Expr\Select($selects), true);
813
    }
814
815
    /**
816
     * Turns the query being built into a bulk delete query that ranges over
817
     * a certain entity type.
818
     *
819
     * <code>
820
     *     $qb = $em->createQueryBuilder()
821
     *         ->delete('User', 'u')
822
     *         ->where('u.id = :user_id')
823
     *         ->setParameter('user_id', 1);
824
     * </code>
825
     *
826
     * @param string $delete The class/type whose instances are subject to the deletion.
827
     * @param string $alias  The class/type alias used in the constructed query.
828
     *
829
     * @return self
830
     */
831 4 View Code Duplication
    public function delete($delete = null, $alias = null)
832
    {
833 4
        $this->_type = self::DELETE;
834
835 4
        if ( ! $delete) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $delete of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
836 1
            return $this;
837
        }
838
839 3
        return $this->add('from', new Expr\From($delete, $alias));
840
    }
841
842
    /**
843
     * Turns the query being built into a bulk update query that ranges over
844
     * a certain entity type.
845
     *
846
     * <code>
847
     *     $qb = $em->createQueryBuilder()
848
     *         ->update('User', 'u')
849
     *         ->set('u.password', '?1')
850
     *         ->where('u.id = ?2');
851
     * </code>
852
     *
853
     * @param string $update The class/type whose instances are subject to the update.
854
     * @param string $alias  The class/type alias used in the constructed query.
855
     *
856
     * @return self
857
     */
858 4 View Code Duplication
    public function update($update = null, $alias = null)
859
    {
860 4
        $this->_type = self::UPDATE;
861
862 4
        if ( ! $update) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $update of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
863 1
            return $this;
864
        }
865
866 3
        return $this->add('from', new Expr\From($update, $alias));
867
    }
868
869
    /**
870
     * Creates and adds a query root corresponding to the entity identified by the given alias,
871
     * forming a cartesian product with any existing query roots.
872
     *
873
     * <code>
874
     *     $qb = $em->createQueryBuilder()
875
     *         ->select('u')
876
     *         ->from('User', 'u');
877
     * </code>
878
     *
879
     * @param string $from    The class name.
880
     * @param string $alias   The alias of the class.
881
     * @param string $indexBy The index for the from.
882
     *
883
     * @return self
884
     */
885 106
    public function from($from, $alias, $indexBy = null)
886
    {
887 106
        return $this->add('from', new Expr\From($from, $alias, $indexBy), true);
888
    }
889
890
    /**
891
     * Updates a query root corresponding to an entity setting its index by. This method is intended to be used with
892
     * EntityRepository->createQueryBuilder(), which creates the initial FROM clause and do not allow you to update it
893
     * setting an index by.
894
     *
895
     * <code>
896
     *     $qb = $userRepository->createQueryBuilder('u')
897
     *         ->indexBy('u', 'u.id');
898
     *
899
     *     // Is equivalent to...
900
     *
901
     *     $qb = $em->createQueryBuilder()
902
     *         ->select('u')
903
     *         ->from('User', 'u', 'u.id');
904
     * </code>
905
     *
906
     * @param string $alias   The root alias of the class.
907
     * @param string $indexBy The index for the from.
908
     *
909
     * @return self
910
     *
911
     * @throws Query\QueryException
912
     */
913 2
    public function indexBy($alias, $indexBy)
914
    {
915 2
        $rootAliases = $this->getRootAliases();
916
917 2
        if (!in_array($alias, $rootAliases)) {
918
            throw new Query\QueryException(
919
                sprintf('Specified root alias %s must be set before invoking indexBy().', $alias)
920
            );
921
        }
922
923 2
        foreach ($this->_dqlParts['from'] as &$fromClause) {
924
            /* @var Expr\From $fromClause */
925 2
            if ($fromClause->getAlias() !== $alias) {
926 1
                continue;
927
            }
928
929 2
            $fromClause = new Expr\From($fromClause->getFrom(), $fromClause->getAlias(), $indexBy);
930
        }
931
932 2
        return $this;
933
    }
934
935
    /**
936
     * Creates and adds a join over an entity association to the query.
937
     *
938
     * The entities in the joined association will be fetched as part of the query
939
     * result if the alias used for the joined association is placed in the select
940
     * expressions.
941
     *
942
     * <code>
943
     *     $qb = $em->createQueryBuilder()
944
     *         ->select('u')
945
     *         ->from('User', 'u')
946
     *         ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
947
     * </code>
948
     *
949
     * @param string      $join          The relationship to join.
950
     * @param string      $alias         The alias of the join.
951
     * @param string|null $conditionType The condition type constant. Either ON or WITH.
952
     * @param string|null $condition     The condition for the join.
953
     * @param string|null $indexBy       The index for the join.
954
     *
955
     * @return self
956
     */
957 8
    public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
958
    {
959 8
        return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy);
960
    }
961
962
    /**
963
     * Creates and adds a join over an entity association to the query.
964
     *
965
     * The entities in the joined association will be fetched as part of the query
966
     * result if the alias used for the joined association is placed in the select
967
     * expressions.
968
     *
969
     *     [php]
970
     *     $qb = $em->createQueryBuilder()
971
     *         ->select('u')
972
     *         ->from('User', 'u')
973
     *         ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
974
     *
975
     * @param string      $join          The relationship to join.
976
     * @param string      $alias         The alias of the join.
977
     * @param string|null $conditionType The condition type constant. Either ON or WITH.
978
     * @param string|null $condition     The condition for the join.
979
     * @param string|null $indexBy       The index for the join.
980
     *
981
     * @return self
982
     */
983 15 View Code Duplication
    public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
984
    {
985 15
        $parentAlias = substr($join, 0, strpos($join, '.'));
986
987 15
        $rootAlias = $this->findRootAlias($alias, $parentAlias);
988
989 15
        $join = new Expr\Join(
990 15
            Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy
991
        );
992
993 15
        return $this->add('join', [$rootAlias => $join], true);
994
    }
995
996
    /**
997
     * Creates and adds a left join over an entity association to the query.
998
     *
999
     * The entities in the joined association will be fetched as part of the query
1000
     * result if the alias used for the joined association is placed in the select
1001
     * expressions.
1002
     *
1003
     * <code>
1004
     *     $qb = $em->createQueryBuilder()
1005
     *         ->select('u')
1006
     *         ->from('User', 'u')
1007
     *         ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
1008
     * </code>
1009
     *
1010
     * @param string      $join          The relationship to join.
1011
     * @param string      $alias         The alias of the join.
1012
     * @param string|null $conditionType The condition type constant. Either ON or WITH.
1013
     * @param string|null $condition     The condition for the join.
1014
     * @param string|null $indexBy       The index for the join.
1015
     *
1016
     * @return self
1017
     */
1018 15 View Code Duplication
    public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
1019
    {
1020 15
        $parentAlias = substr($join, 0, strpos($join, '.'));
1021
1022 15
        $rootAlias = $this->findRootAlias($alias, $parentAlias);
1023
1024 15
        $join = new Expr\Join(
1025 15
            Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy
1026
        );
1027
1028 15
        return $this->add('join', [$rootAlias => $join], true);
1029
    }
1030
1031
    /**
1032
     * Sets a new value for a field in a bulk update query.
1033
     *
1034
     * <code>
1035
     *     $qb = $em->createQueryBuilder()
1036
     *         ->update('User', 'u')
1037
     *         ->set('u.password', '?1')
1038
     *         ->where('u.id = ?2');
1039
     * </code>
1040
     *
1041
     * @param string $key   The key/field to set.
1042
     * @param string $value The value, expression, placeholder, etc.
1043
     *
1044
     * @return self
1045
     */
1046 3
    public function set($key, $value)
1047
    {
1048 3
        return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true);
1049
    }
1050
1051
    /**
1052
     * Specifies one or more restrictions to the query result.
1053
     * Replaces any previously specified restrictions, if any.
1054
     *
1055
     * <code>
1056
     *     $qb = $em->createQueryBuilder()
1057
     *         ->select('u')
1058
     *         ->from('User', 'u')
1059
     *         ->where('u.id = ?');
1060
     *
1061
     *     // You can optionally programmatically build and/or expressions
1062
     *     $qb = $em->createQueryBuilder();
1063
     *
1064
     *     $or = $qb->expr()->orX();
1065
     *     $or->add($qb->expr()->eq('u.id', 1));
1066
     *     $or->add($qb->expr()->eq('u.id', 2));
1067
     *
1068
     *     $qb->update('User', 'u')
1069
     *         ->set('u.password', '?')
1070
     *         ->where($or);
1071
     * </code>
1072
     *
1073
     * @param mixed $predicates The restriction predicates.
1074
     *
1075
     * @return self
1076
     */
1077 42 View Code Duplication
    public function where($predicates)
1078
    {
1079 42
        if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) {
1080 39
            $predicates = new Expr\Andx(func_get_args());
1081
        }
1082
1083 42
        return $this->add('where', $predicates);
1084
    }
1085
1086
    /**
1087
     * Adds one or more restrictions to the query results, forming a logical
1088
     * conjunction with any previously specified restrictions.
1089
     *
1090
     * <code>
1091
     *     $qb = $em->createQueryBuilder()
1092
     *         ->select('u')
1093
     *         ->from('User', 'u')
1094
     *         ->where('u.username LIKE ?')
1095
     *         ->andWhere('u.is_active = 1');
1096
     * </code>
1097
     *
1098
     * @param mixed $where The query restrictions.
1099
     *
1100
     * @return self
1101
     *
1102
     * @see where()
1103
     */
1104 17 View Code Duplication
    public function andWhere()
1105
    {
1106 17
        $args  = func_get_args();
1107 17
        $where = $this->getDQLPart('where');
1108
1109 17
        if ($where instanceof Expr\Andx) {
1110 6
            $where->addMultiple($args);
1111
        } else {
1112 12
            array_unshift($args, $where);
1113 12
            $where = new Expr\Andx($args);
1114
        }
1115
1116 17
        return $this->add('where', $where);
1117
    }
1118
1119
    /**
1120
     * Adds one or more restrictions to the query results, forming a logical
1121
     * disjunction with any previously specified restrictions.
1122
     *
1123
     * <code>
1124
     *     $qb = $em->createQueryBuilder()
1125
     *         ->select('u')
1126
     *         ->from('User', 'u')
1127
     *         ->where('u.id = 1')
1128
     *         ->orWhere('u.id = 2');
1129
     * </code>
1130
     *
1131
     * @param mixed $where The WHERE statement.
1132
     *
1133
     * @return self
1134
     *
1135
     * @see where()
1136
     */
1137 5 View Code Duplication
    public function orWhere()
1138
    {
1139 5
        $args  = func_get_args();
1140 5
        $where = $this->getDQLPart('where');
1141
1142 5
        if ($where instanceof Expr\Orx) {
1143
            $where->addMultiple($args);
1144
        } else {
1145 5
            array_unshift($args, $where);
1146 5
            $where = new Expr\Orx($args);
1147
        }
1148
1149 5
        return $this->add('where', $where);
1150
    }
1151
1152
    /**
1153
     * Specifies a grouping over the results of the query.
1154
     * Replaces any previously specified groupings, if any.
1155
     *
1156
     * <code>
1157
     *     $qb = $em->createQueryBuilder()
1158
     *         ->select('u')
1159
     *         ->from('User', 'u')
1160
     *         ->groupBy('u.id');
1161
     * </code>
1162
     *
1163
     * @param string $groupBy The grouping expression.
1164
     *
1165
     * @return self
1166
     */
1167 7
    public function groupBy($groupBy)
0 ignored issues
show
The parameter $groupBy is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1168
    {
1169 7
        return $this->add('groupBy', new Expr\GroupBy(func_get_args()));
1170
    }
1171
1172
    /**
1173
     * Adds a grouping expression to the query.
1174
     *
1175
     * <code>
1176
     *     $qb = $em->createQueryBuilder()
1177
     *         ->select('u')
1178
     *         ->from('User', 'u')
1179
     *         ->groupBy('u.lastLogin')
1180
     *         ->addGroupBy('u.createdAt');
1181
     * </code>
1182
     *
1183
     * @param string $groupBy The grouping expression.
1184
     *
1185
     * @return self
1186
     */
1187 1
    public function addGroupBy($groupBy)
0 ignored issues
show
The parameter $groupBy is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1188
    {
1189 1
        return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true);
1190
    }
1191
1192
    /**
1193
     * Specifies a restriction over the groups of the query.
1194
     * Replaces any previous having restrictions, if any.
1195
     *
1196
     * @param mixed $having The restriction over the groups.
1197
     *
1198
     * @return self
1199
     */
1200 3 View Code Duplication
    public function having($having)
1201
    {
1202 3
        if ( ! (func_num_args() == 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) {
1203 3
            $having = new Expr\Andx(func_get_args());
1204
        }
1205
1206 3
        return $this->add('having', $having);
1207
    }
1208
1209
    /**
1210
     * Adds a restriction over the groups of the query, forming a logical
1211
     * conjunction with any existing having restrictions.
1212
     *
1213
     * @param mixed $having The restriction to append.
1214
     *
1215
     * @return self
1216
     */
1217 2 View Code Duplication
    public function andHaving($having)
0 ignored issues
show
The parameter $having is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1218
    {
1219 2
        $args   = func_get_args();
1220 2
        $having = $this->getDQLPart('having');
1221
1222 2
        if ($having instanceof Expr\Andx) {
1223 2
            $having->addMultiple($args);
1224
        } else {
1225
            array_unshift($args, $having);
1226
            $having = new Expr\Andx($args);
1227
        }
1228
1229 2
        return $this->add('having', $having);
1230
    }
1231
1232
    /**
1233
     * Adds a restriction over the groups of the query, forming a logical
1234
     * disjunction with any existing having restrictions.
1235
     *
1236
     * @param mixed $having The restriction to add.
1237
     *
1238
     * @return self
1239
     */
1240 1 View Code Duplication
    public function orHaving($having)
0 ignored issues
show
The parameter $having is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1241
    {
1242 1
        $args   = func_get_args();
1243 1
        $having = $this->getDQLPart('having');
1244
1245 1
        if ($having instanceof Expr\Orx) {
1246
            $having->addMultiple($args);
1247
        } else {
1248 1
            array_unshift($args, $having);
1249 1
            $having = new Expr\Orx($args);
1250
        }
1251
1252 1
        return $this->add('having', $having);
1253
    }
1254
1255
    /**
1256
     * Specifies an ordering for the query results.
1257
     * Replaces any previously specified orderings, if any.
1258
     *
1259
     * @param string|Expr\OrderBy $sort  The ordering expression.
1260
     * @param string              $order The ordering direction.
1261
     *
1262
     * @return self
1263
     */
1264 10
    public function orderBy($sort, $order = null)
1265
    {
1266 10
        $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order);
1267
1268 10
        return $this->add('orderBy', $orderBy);
1269
    }
1270
1271
    /**
1272
     * Adds an ordering to the query results.
1273
     *
1274
     * @param string|Expr\OrderBy $sort  The ordering expression.
1275
     * @param string              $order The ordering direction.
1276
     *
1277
     * @return self
1278
     */
1279 4
    public function addOrderBy($sort, $order = null)
1280
    {
1281 4
        $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order);
1282
1283 4
        return $this->add('orderBy', $orderBy, true);
1284
    }
1285
1286
    /**
1287
     * Adds criteria to the query.
1288
     *
1289
     * Adds where expressions with AND operator.
1290
     * Adds orderings.
1291
     * Overrides firstResult and maxResults if they're set.
1292
     *
1293
     * @param Criteria $criteria
1294
     *
1295
     * @return self
1296
     *
1297
     * @throws Query\QueryException
1298
     */
1299 13
    public function addCriteria(Criteria $criteria)
1300
    {
1301 13
        $allAliases = $this->getAllAliases();
1302 13
        if ( ! isset($allAliases[0])) {
1303
            throw new Query\QueryException('No aliases are set before invoking addCriteria().');
1304
        }
1305
1306 13
        $visitor = new QueryExpressionVisitor($this->getAllAliases());
1307
1308 13
        if ($whereExpression = $criteria->getWhereExpression()) {
1309 9
            $this->andWhere($visitor->dispatch($whereExpression));
1310 9
            foreach ($visitor->getParameters() as $parameter) {
1311 9
                $this->parameters->add($parameter);
1312
            }
1313
        }
1314
1315 13
        if ($criteria->getOrderings()) {
1316 2
            foreach ($criteria->getOrderings() as $sort => $order) {
1317
1318 2
                $hasValidAlias = false;
1319 2
                foreach($allAliases as $alias) {
1320 2
                    if(strpos($sort . '.', $alias . '.') === 0) {
1321 1
                        $hasValidAlias = true;
1322 2
                        break;
1323
                    }
1324
                }
1325
1326 2
                if(!$hasValidAlias) {
1327 1
                    $sort = $allAliases[0] . '.' . $sort;
1328
                }
1329
1330 2
                $this->addOrderBy($sort, $order);
1331
            }
1332
        }
1333
1334
        // Overwrite limits only if they was set in criteria
1335 13
        if (($firstResult = $criteria->getFirstResult()) !== null) {
1336 1
            $this->setFirstResult($firstResult);
1337
        }
1338 13
        if (($maxResults = $criteria->getMaxResults()) !== null) {
1339 1
            $this->setMaxResults($maxResults);
1340
        }
1341
1342 13
        return $this;
1343
    }
1344
1345
    /**
1346
     * Gets a query part by its name.
1347
     *
1348
     * @param string $queryPartName
1349
     *
1350
     * @return mixed $queryPart
1351
     *
1352
     * @todo Rename: getQueryPart (or remove?)
1353
     */
1354 97
    public function getDQLPart($queryPartName)
1355
    {
1356 97
        return $this->_dqlParts[$queryPartName];
1357
    }
1358
1359
    /**
1360
     * Gets all query parts.
1361
     *
1362
     * @return array $dqlParts
1363
     *
1364
     * @todo Rename: getQueryParts (or remove?)
1365
     */
1366 1
    public function getDQLParts()
1367
    {
1368 1
        return $this->_dqlParts;
1369
    }
1370
1371
    /**
1372
     * @return string
1373
     */
1374 1
    private function _getDQLForDelete()
1375
    {
1376
         return 'DELETE'
1377 1
              . $this->_getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])
1378 1
              . $this->_getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
1379 1
              . $this->_getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
1380
    }
1381
1382
    /**
1383
     * @return string
1384
     */
1385 3
    private function _getDQLForUpdate()
1386
    {
1387
         return 'UPDATE'
1388 3
              . $this->_getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', '])
1389 3
              . $this->_getReducedDQLQueryPart('set', ['pre' => ' SET ', 'separator' => ', '])
1390 3
              . $this->_getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
1391 3
              . $this->_getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
1392
    }
1393
1394
    /**
1395
     * @return string
1396
     */
1397 79
    private function _getDQLForSelect()
1398
    {
1399
        $dql = 'SELECT'
1400 79
             . ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '')
1401 79
             . $this->_getReducedDQLQueryPart('select', ['pre' => ' ', 'separator' => ', ']);
1402
1403 79
        $fromParts   = $this->getDQLPart('from');
1404 79
        $joinParts   = $this->getDQLPart('join');
1405 79
        $fromClauses = [];
1406
1407
        // Loop through all FROM clauses
1408 79
        if ( ! empty($fromParts)) {
1409 78
            $dql .= ' FROM ';
1410
1411 78
            foreach ($fromParts as $from) {
1412 78
                $fromClause = (string) $from;
1413
1414 78
                if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) {
1415 25
                    foreach ($joinParts[$from->getAlias()] as $join) {
1416 25
                        $fromClause .= ' ' . ((string) $join);
1417
                    }
1418
                }
1419
1420 78
                $fromClauses[] = $fromClause;
1421
            }
1422
        }
1423
1424 79
        $dql .= implode(', ', $fromClauses)
1425 79
              . $this->_getReducedDQLQueryPart('where', ['pre' => ' WHERE '])
1426 79
              . $this->_getReducedDQLQueryPart('groupBy', ['pre' => ' GROUP BY ', 'separator' => ', '])
1427 79
              . $this->_getReducedDQLQueryPart('having', ['pre' => ' HAVING '])
1428 79
              . $this->_getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']);
1429
1430 79
        return $dql;
1431
    }
1432
1433
    /**
1434
     * @param string $queryPartName
1435
     * @param array  $options
1436
     *
1437
     * @return string
1438
     */
1439 82
    private function _getReducedDQLQueryPart($queryPartName, $options = [])
1440
    {
1441 82
        $queryPart = $this->getDQLPart($queryPartName);
1442
1443 82
        if (empty($queryPart)) {
1444 82
            return (isset($options['empty']) ? $options['empty'] : '');
1445
        }
1446
1447 82
        return (isset($options['pre']) ? $options['pre'] : '')
1448 82
             . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart)
1449 82
             . (isset($options['post']) ? $options['post'] : '');
1450
    }
1451
1452
    /**
1453
     * Resets DQL parts.
1454
     *
1455
     * @param array|null $parts
1456
     *
1457
     * @return self
1458
     */
1459 2
    public function resetDQLParts($parts = null)
1460
    {
1461 2
        if (null === $parts) {
1462 1
            $parts = array_keys($this->_dqlParts);
1463
        }
1464
1465 2
        foreach ($parts as $part) {
1466 2
            $this->resetDQLPart($part);
1467
        }
1468
1469 2
        return $this;
1470
    }
1471
1472
    /**
1473
     * Resets single DQL part.
1474
     *
1475
     * @param string $part
1476
     *
1477
     * @return self
1478
     */
1479 3
    public function resetDQLPart($part)
1480
    {
1481 3
        $this->_dqlParts[$part] = is_array($this->_dqlParts[$part]) ? [] : null;
1482 3
        $this->_state           = self::STATE_DIRTY;
1483
1484 3
        return $this;
1485
    }
1486
1487
    /**
1488
     * Gets a string representation of this QueryBuilder which corresponds to
1489
     * the final DQL query being constructed.
1490
     *
1491
     * @return string The string representation of this QueryBuilder.
1492
     */
1493 5
    public function __toString()
1494
    {
1495 5
        return $this->getDQL();
1496
    }
1497
1498
    /**
1499
     * Deep clones all expression objects in the DQL parts.
1500
     *
1501
     * @return void
1502
     */
1503 3
    public function __clone()
1504
    {
1505 3
        foreach ($this->_dqlParts as $part => $elements) {
1506 3
            if (is_array($this->_dqlParts[$part])) {
1507 3
                foreach ($this->_dqlParts[$part] as $idx => $element) {
1508 2
                    if (is_object($element)) {
1509 3
                        $this->_dqlParts[$part][$idx] = clone $element;
1510
                    }
1511
                }
1512 3
            } else if (is_object($elements)) {
1513 3
                $this->_dqlParts[$part] = clone $elements;
1514
            }
1515
        }
1516
1517 3
        $parameters = [];
1518
1519 3
        foreach ($this->parameters as $parameter) {
1520 1
            $parameters[] = clone $parameter;
1521
        }
1522
1523 3
        $this->parameters = new ArrayCollection($parameters);
1524 3
    }
1525
}
1526