1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Doctrine\ORM; |
||
6 | |||
7 | use Doctrine\Common\Collections\ArrayCollection; |
||
8 | use Doctrine\Common\Collections\Criteria; |
||
9 | |||
10 | use Doctrine\ORM\Query\Expr; |
||
11 | use Doctrine\ORM\Query\QueryExpressionVisitor; |
||
12 | |||
13 | /** |
||
14 | * This class is responsible for building DQL query strings via an object oriented |
||
15 | * PHP interface. |
||
16 | */ |
||
17 | class QueryBuilder |
||
18 | { |
||
19 | /* The query types. */ |
||
20 | public const SELECT = 0; |
||
21 | public const DELETE = 1; |
||
22 | public const UPDATE = 2; |
||
23 | |||
24 | /* The builder states. */ |
||
25 | public const STATE_DIRTY = 0; |
||
26 | public const STATE_CLEAN = 1; |
||
27 | |||
28 | /** |
||
29 | * The EntityManager used by this QueryBuilder. |
||
30 | * |
||
31 | * @var EntityManagerInterface |
||
32 | */ |
||
33 | private $em; |
||
34 | |||
35 | /** |
||
36 | * The array of DQL parts collected. |
||
37 | * |
||
38 | * @var mixed[] |
||
39 | */ |
||
40 | private $dqlParts = [ |
||
41 | 'distinct' => false, |
||
42 | 'select' => [], |
||
43 | 'from' => [], |
||
44 | 'join' => [], |
||
45 | 'set' => [], |
||
46 | 'where' => null, |
||
47 | 'groupBy' => [], |
||
48 | 'having' => null, |
||
49 | 'orderBy' => [], |
||
50 | ]; |
||
51 | |||
52 | /** |
||
53 | * The type of query this is. Can be select, update or delete. |
||
54 | * |
||
55 | * @var int |
||
56 | */ |
||
57 | private $type = self::SELECT; |
||
58 | |||
59 | /** |
||
60 | * The state of the query object. Can be dirty or clean. |
||
61 | * |
||
62 | * @var int |
||
63 | */ |
||
64 | private $state = self::STATE_CLEAN; |
||
65 | |||
66 | /** |
||
67 | * The complete DQL string for this query. |
||
68 | * |
||
69 | * @var string |
||
70 | */ |
||
71 | private $dql; |
||
72 | |||
73 | /** |
||
74 | * The query parameters. |
||
75 | * |
||
76 | * @var ArrayCollection |
||
77 | */ |
||
78 | private $parameters; |
||
79 | |||
80 | /** |
||
81 | * The index of the first result to retrieve. |
||
82 | * |
||
83 | * @var int |
||
84 | */ |
||
85 | private $firstResult; |
||
86 | |||
87 | /** |
||
88 | * The maximum number of results to retrieve. |
||
89 | * |
||
90 | * @var int|null |
||
91 | */ |
||
92 | private $maxResults; |
||
93 | |||
94 | /** |
||
95 | * Keeps root entity alias names for join entities. |
||
96 | * |
||
97 | * @var mixed[] |
||
98 | */ |
||
99 | private $joinRootAliases = []; |
||
100 | |||
101 | /** |
||
102 | * Whether to use second level cache, if available. |
||
103 | * |
||
104 | * @var bool |
||
105 | */ |
||
106 | protected $cacheable = false; |
||
107 | |||
108 | /** |
||
109 | * Second level cache region name. |
||
110 | * |
||
111 | * @var string|null |
||
112 | */ |
||
113 | protected $cacheRegion; |
||
114 | |||
115 | /** |
||
116 | * Second level query cache mode. |
||
117 | * |
||
118 | * @var int|null |
||
119 | */ |
||
120 | protected $cacheMode; |
||
121 | |||
122 | /** |
||
123 | * @var int |
||
124 | */ |
||
125 | protected $lifetime = 0; |
||
126 | |||
127 | /** |
||
128 | * Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>. |
||
129 | * |
||
130 | * @param EntityManagerInterface $em The EntityManager to use. |
||
131 | */ |
||
132 | 127 | public function __construct(EntityManagerInterface $em) |
|
133 | { |
||
134 | 127 | $this->em = $em; |
|
135 | 127 | $this->parameters = new ArrayCollection(); |
|
136 | 127 | } |
|
137 | |||
138 | /** |
||
139 | * Gets an ExpressionBuilder used for object-oriented construction of query expressions. |
||
140 | * This producer method is intended for convenient inline usage. Example: |
||
141 | * |
||
142 | * <code> |
||
143 | * $qb = $em->createQueryBuilder(); |
||
144 | * $qb |
||
145 | * ->select('u') |
||
146 | * ->from('User', '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 Query\Expr |
||
154 | */ |
||
155 | 11 | public function expr() |
|
156 | { |
||
157 | 11 | return $this->em->getExpressionBuilder(); |
|
158 | } |
||
159 | |||
160 | /** |
||
161 | * Enable/disable second level query (result) caching for this query. |
||
162 | * |
||
163 | * @param bool $cacheable |
||
164 | * |
||
165 | * @return self |
||
166 | */ |
||
167 | 1 | public function setCacheable($cacheable) |
|
168 | { |
||
169 | 1 | $this->cacheable = (bool) $cacheable; |
|
170 | |||
171 | 1 | return $this; |
|
172 | } |
||
173 | |||
174 | /** |
||
175 | * @return bool TRUE if the query results are enable for second level cache, FALSE otherwise. |
||
176 | */ |
||
177 | 1 | public function isCacheable() |
|
178 | { |
||
179 | 1 | return $this->cacheable; |
|
180 | } |
||
181 | |||
182 | /** |
||
183 | * @param string $cacheRegion |
||
184 | * |
||
185 | * @return self |
||
186 | */ |
||
187 | 1 | public function setCacheRegion($cacheRegion) |
|
188 | { |
||
189 | 1 | $this->cacheRegion = (string) $cacheRegion; |
|
190 | |||
191 | 1 | return $this; |
|
192 | } |
||
193 | |||
194 | /** |
||
195 | * Obtain the name of the second level query cache region in which query results will be stored |
||
196 | * |
||
197 | * @return string|null The cache region name; NULL indicates the default region. |
||
198 | */ |
||
199 | 1 | public function getCacheRegion() |
|
200 | { |
||
201 | 1 | return $this->cacheRegion; |
|
202 | } |
||
203 | |||
204 | /** |
||
205 | * @return int |
||
206 | */ |
||
207 | 1 | public function getLifetime() |
|
208 | { |
||
209 | 1 | return $this->lifetime; |
|
210 | } |
||
211 | |||
212 | /** |
||
213 | * Sets the life-time for this query into second level cache. |
||
214 | * |
||
215 | * @param int $lifetime |
||
216 | * |
||
217 | * @return self |
||
218 | */ |
||
219 | 1 | public function setLifetime($lifetime) |
|
220 | { |
||
221 | 1 | $this->lifetime = (int) $lifetime; |
|
222 | |||
223 | 1 | return $this; |
|
224 | } |
||
225 | |||
226 | /** |
||
227 | * @return int |
||
228 | */ |
||
229 | 1 | public function getCacheMode() |
|
230 | { |
||
231 | 1 | return $this->cacheMode; |
|
232 | } |
||
233 | |||
234 | /** |
||
235 | * @param int $cacheMode |
||
236 | * |
||
237 | * @return self |
||
238 | */ |
||
239 | 1 | public function setCacheMode($cacheMode) |
|
240 | { |
||
241 | 1 | $this->cacheMode = (int) $cacheMode; |
|
242 | |||
243 | 1 | return $this; |
|
244 | } |
||
245 | |||
246 | /** |
||
247 | * Gets the type of the currently built query. |
||
248 | * |
||
249 | * @return int |
||
250 | */ |
||
251 | 4 | public function getType() |
|
252 | { |
||
253 | 4 | return $this->type; |
|
254 | } |
||
255 | |||
256 | /** |
||
257 | * Gets the associated EntityManager for this query builder. |
||
258 | * |
||
259 | * @return EntityManagerInterface |
||
260 | */ |
||
261 | 1 | public function getEntityManager() |
|
262 | { |
||
263 | 1 | return $this->em; |
|
264 | } |
||
265 | |||
266 | /** |
||
267 | * Gets the state of this query builder instance. |
||
268 | * |
||
269 | * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. |
||
270 | */ |
||
271 | 2 | public function getState() |
|
272 | { |
||
273 | 2 | return $this->state; |
|
274 | } |
||
275 | |||
276 | /** |
||
277 | * Gets the complete DQL string formed by the current specifications of this QueryBuilder. |
||
278 | * |
||
279 | * <code> |
||
280 | * $qb = $em->createQueryBuilder() |
||
281 | * ->select('u') |
||
282 | * ->from('User', 'u'); |
||
283 | * echo $qb->getDql(); // SELECT u FROM User u |
||
284 | * </code> |
||
285 | * |
||
286 | * @return string The DQL query string. |
||
287 | */ |
||
288 | 87 | public function getDQL() |
|
289 | { |
||
290 | 87 | if ($this->dql !== null && $this->state === self::STATE_CLEAN) { |
|
291 | 44 | return $this->dql; |
|
292 | } |
||
293 | |||
294 | 87 | switch ($this->type) { |
|
295 | 87 | case self::DELETE: |
|
296 | 1 | $dql = $this->getDQLForDelete(); |
|
297 | 1 | break; |
|
298 | |||
299 | 86 | case self::UPDATE: |
|
300 | 3 | $dql = $this->getDQLForUpdate(); |
|
301 | 3 | break; |
|
302 | |||
303 | 84 | case self::SELECT: |
|
304 | default: |
||
305 | 84 | $dql = $this->getDQLForSelect(); |
|
306 | 84 | break; |
|
307 | } |
||
308 | |||
309 | 87 | $this->state = self::STATE_CLEAN; |
|
310 | 87 | $this->dql = $dql; |
|
311 | |||
312 | 87 | return $dql; |
|
313 | } |
||
314 | |||
315 | /** |
||
316 | * Constructs a Query instance from the current specifications of the builder. |
||
317 | * |
||
318 | * <code> |
||
319 | * $qb = $em->createQueryBuilder() |
||
320 | * ->select('u') |
||
321 | * ->from('User', 'u'); |
||
322 | * $q = $qb->getQuery(); |
||
323 | * $results = $q->execute(); |
||
324 | * </code> |
||
325 | * |
||
326 | * @return Query |
||
327 | */ |
||
328 | 76 | public function getQuery() |
|
329 | { |
||
330 | 76 | $parameters = clone $this->parameters; |
|
331 | 76 | $query = $this->em->createQuery($this->getDQL()) |
|
332 | 76 | ->setParameters($parameters) |
|
333 | 76 | ->setFirstResult($this->firstResult) |
|
334 | 76 | ->setMaxResults($this->maxResults); |
|
335 | |||
336 | 76 | if ($this->lifetime) { |
|
337 | 1 | $query->setLifetime($this->lifetime); |
|
338 | } |
||
339 | |||
340 | 76 | if ($this->cacheMode) { |
|
341 | 1 | $query->setCacheMode($this->cacheMode); |
|
342 | } |
||
343 | |||
344 | 76 | if ($this->cacheable) { |
|
345 | 1 | $query->setCacheable($this->cacheable); |
|
346 | } |
||
347 | |||
348 | 76 | if ($this->cacheRegion) { |
|
349 | 1 | $query->setCacheRegion($this->cacheRegion); |
|
350 | } |
||
351 | |||
352 | 76 | return $query; |
|
353 | } |
||
354 | |||
355 | /** |
||
356 | * Finds the root entity alias of the joined entity. |
||
357 | * |
||
358 | * @param string $alias The alias of the new join entity |
||
359 | * @param string $parentAlias The parent entity alias of the join relationship |
||
360 | * |
||
361 | * @return string |
||
362 | */ |
||
363 | 30 | private function findRootAlias($alias, $parentAlias) |
|
364 | { |
||
365 | 30 | $rootAlias = null; |
|
366 | |||
367 | 30 | if (in_array($parentAlias, $this->getRootAliases())) { |
|
368 | 29 | $rootAlias = $parentAlias; |
|
369 | 7 | } elseif (isset($this->joinRootAliases[$parentAlias])) { |
|
370 | 6 | $rootAlias = $this->joinRootAliases[$parentAlias]; |
|
371 | } else { |
||
372 | // Should never happen with correct joining order. Might be |
||
373 | // thoughtful to throw exception instead. |
||
374 | 1 | $rootAlias = $this->getRootAlias(); |
|
0 ignored issues
–
show
|
|||
375 | } |
||
376 | |||
377 | 30 | $this->joinRootAliases[$alias] = $rootAlias; |
|
378 | |||
379 | 30 | return $rootAlias; |
|
380 | } |
||
381 | |||
382 | /** |
||
383 | * Gets the FIRST root alias of the query. This is the first entity alias involved |
||
384 | * in the construction of the query. |
||
385 | * |
||
386 | * <code> |
||
387 | * $qb = $em->createQueryBuilder() |
||
388 | * ->select('u') |
||
389 | * ->from('User', 'u'); |
||
390 | * |
||
391 | * echo $qb->getRootAlias(); // u |
||
392 | * </code> |
||
393 | * |
||
394 | * @deprecated Please use $qb->getRootAliases() instead. |
||
395 | * @throws \RuntimeException |
||
396 | * |
||
397 | * @return string |
||
398 | */ |
||
399 | 4 | public function getRootAlias() |
|
400 | { |
||
401 | 4 | $aliases = $this->getRootAliases(); |
|
402 | |||
403 | 4 | if (! isset($aliases[0])) { |
|
404 | throw new \RuntimeException('No alias was set before invoking getRootAlias().'); |
||
405 | } |
||
406 | |||
407 | 4 | return $aliases[0]; |
|
408 | } |
||
409 | |||
410 | /** |
||
411 | * Gets the root aliases of the query. This is the entity aliases involved |
||
412 | * in the construction of the query. |
||
413 | * |
||
414 | * <code> |
||
415 | * $qb = $em->createQueryBuilder() |
||
416 | * ->select('u') |
||
417 | * ->from('User', 'u'); |
||
418 | * |
||
419 | * $qb->getRootAliases(); // array('u') |
||
420 | * </code> |
||
421 | * |
||
422 | * @return string[] |
||
423 | */ |
||
424 | 46 | public function getRootAliases() |
|
425 | { |
||
426 | 46 | $aliases = []; |
|
427 | |||
428 | 46 | foreach ($this->dqlParts['from'] as &$fromClause) { |
|
429 | 46 | if (is_string($fromClause)) { |
|
430 | $spacePos = strrpos($fromClause, ' '); |
||
431 | $from = substr($fromClause, 0, $spacePos); |
||
432 | $alias = substr($fromClause, $spacePos + 1); |
||
433 | |||
434 | $fromClause = new Query\Expr\From($from, $alias); |
||
435 | } |
||
436 | |||
437 | 46 | $aliases[] = $fromClause->getAlias(); |
|
438 | } |
||
439 | |||
440 | 46 | return $aliases; |
|
441 | } |
||
442 | |||
443 | /** |
||
444 | * Gets all the aliases that have been used in the query. |
||
445 | * Including all select root aliases and join aliases |
||
446 | * |
||
447 | * <code> |
||
448 | * $qb = $em->createQueryBuilder() |
||
449 | * ->select('u') |
||
450 | * ->from('User', 'u') |
||
451 | * ->join('u.articles','a'); |
||
452 | * |
||
453 | * $qb->getAllAliases(); // array('u','a') |
||
454 | * </code> |
||
455 | * |
||
456 | * @return string[] |
||
457 | */ |
||
458 | 15 | public function getAllAliases() |
|
459 | { |
||
460 | 15 | return array_merge($this->getRootAliases(), array_keys($this->joinRootAliases)); |
|
461 | } |
||
462 | |||
463 | /** |
||
464 | * Gets the root entities of the query. This is the entity aliases involved |
||
465 | * in the construction of the query. |
||
466 | * |
||
467 | * <code> |
||
468 | * $qb = $em->createQueryBuilder() |
||
469 | * ->select('u') |
||
470 | * ->from('User', 'u'); |
||
471 | * |
||
472 | * $qb->getRootEntities(); // array('User') |
||
473 | * </code> |
||
474 | * |
||
475 | * @return string[] |
||
476 | */ |
||
477 | 1 | public function getRootEntities() |
|
478 | { |
||
479 | 1 | $entities = []; |
|
480 | |||
481 | 1 | foreach ($this->dqlParts['from'] as &$fromClause) { |
|
482 | 1 | if (is_string($fromClause)) { |
|
483 | $spacePos = strrpos($fromClause, ' '); |
||
484 | $from = substr($fromClause, 0, $spacePos); |
||
485 | $alias = substr($fromClause, $spacePos + 1); |
||
486 | |||
487 | $fromClause = new Query\Expr\From($from, $alias); |
||
488 | } |
||
489 | |||
490 | 1 | $entities[] = $fromClause->getFrom(); |
|
491 | } |
||
492 | |||
493 | 1 | return $entities; |
|
494 | } |
||
495 | |||
496 | /** |
||
497 | * Sets a query parameter for the query being constructed. |
||
498 | * |
||
499 | * <code> |
||
500 | * $qb = $em->createQueryBuilder() |
||
501 | * ->select('u') |
||
502 | * ->from('User', 'u') |
||
503 | * ->where('u.id = :user_id') |
||
504 | * ->setParameter('user_id', 1); |
||
505 | * </code> |
||
506 | * |
||
507 | * @param string|int $key The parameter position or name. |
||
508 | * @param mixed $value The parameter value. |
||
509 | * @param string|int|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant |
||
510 | * |
||
511 | * @return self |
||
512 | */ |
||
513 | 17 | public function setParameter($key, $value, $type = null) |
|
514 | { |
||
515 | 17 | $existingParameter = $this->getParameter($key); |
|
516 | |||
517 | 17 | if ($existingParameter !== null) { |
|
518 | 1 | $existingParameter->setValue($value, $type); |
|
519 | |||
520 | 1 | return $this; |
|
521 | } |
||
522 | |||
523 | 17 | $this->parameters->add(new Query\Parameter($key, $value, $type)); |
|
524 | |||
525 | 17 | return $this; |
|
526 | } |
||
527 | |||
528 | /** |
||
529 | * Sets a collection of query parameters for the query being constructed. |
||
530 | * |
||
531 | * <code> |
||
532 | * $qb = $em->createQueryBuilder() |
||
533 | * ->select('u') |
||
534 | * ->from('User', 'u') |
||
535 | * ->where('u.id = :user_id1 OR u.id = :user_id2') |
||
536 | * ->setParameters(new ArrayCollection(array( |
||
537 | * new Parameter('user_id1', 1), |
||
538 | * new Parameter('user_id2', 2) |
||
539 | * ))); |
||
540 | * </code> |
||
541 | * |
||
542 | * @param \Doctrine\Common\Collections\ArrayCollection|array|mixed[] $parameters The query parameters to set. |
||
543 | * |
||
544 | * @return self |
||
545 | */ |
||
546 | 4 | public function setParameters($parameters) |
|
547 | { |
||
548 | // BC compatibility with 2.3- |
||
549 | 4 | if (is_array($parameters)) { |
|
550 | 1 | $parameterCollection = new ArrayCollection(); |
|
551 | |||
552 | 1 | foreach ($parameters as $key => $value) { |
|
553 | 1 | $parameter = new Query\Parameter($key, $value); |
|
554 | |||
555 | 1 | $parameterCollection->add($parameter); |
|
556 | } |
||
557 | |||
558 | 1 | $parameters = $parameterCollection; |
|
559 | } |
||
560 | |||
561 | 4 | $this->parameters = $parameters; |
|
562 | |||
563 | 4 | return $this; |
|
564 | } |
||
565 | |||
566 | /** |
||
567 | * Gets all defined query parameters for the query being constructed. |
||
568 | * |
||
569 | * @return \Doctrine\Common\Collections\ArrayCollection The currently defined query parameters. |
||
570 | */ |
||
571 | 6 | public function getParameters() |
|
572 | { |
||
573 | 6 | return $this->parameters; |
|
574 | } |
||
575 | |||
576 | /** |
||
577 | * Gets a (previously set) query parameter of the query being constructed. |
||
578 | * |
||
579 | * @param mixed $key The key (index or name) of the bound parameter. |
||
580 | * |
||
581 | * @return Query\Parameter|null The value of the bound parameter. |
||
582 | */ |
||
583 | 27 | public function getParameter($key) |
|
584 | { |
||
585 | 27 | $filteredParameters = $this->parameters->filter( |
|
586 | 27 | function (Query\Parameter $parameter) use ($key) : bool { |
|
587 | 19 | $parameterName = $parameter->getName(); |
|
588 | |||
589 | 19 | return $key === $parameterName || (string) $key === (string) $parameterName; |
|
590 | 27 | } |
|
591 | ); |
||
592 | |||
593 | 27 | return $filteredParameters->isEmpty() ? null : $filteredParameters->first(); |
|
594 | } |
||
595 | |||
596 | /** |
||
597 | * Sets the position of the first result to retrieve (the "offset"). |
||
598 | * |
||
599 | * @param int $firstResult The first result to return. |
||
600 | * |
||
601 | * @return self |
||
602 | */ |
||
603 | 2 | public function setFirstResult($firstResult) |
|
604 | { |
||
605 | 2 | $this->firstResult = $firstResult; |
|
606 | |||
607 | 2 | return $this; |
|
608 | } |
||
609 | |||
610 | /** |
||
611 | * Gets the position of the first result the query object was set to retrieve (the "offset"). |
||
612 | * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. |
||
613 | * |
||
614 | * @return int The position of the first result. |
||
615 | */ |
||
616 | 2 | public function getFirstResult() |
|
617 | { |
||
618 | 2 | return $this->firstResult; |
|
619 | } |
||
620 | |||
621 | /** |
||
622 | * Sets the maximum number of results to retrieve (the "limit"). |
||
623 | * |
||
624 | * @param int|null $maxResults The maximum number of results to retrieve. |
||
625 | * |
||
626 | * @return self |
||
627 | */ |
||
628 | 3 | public function setMaxResults($maxResults) |
|
629 | { |
||
630 | 3 | $this->maxResults = $maxResults; |
|
631 | |||
632 | 3 | return $this; |
|
633 | } |
||
634 | |||
635 | /** |
||
636 | * Gets the maximum number of results the query object was set to retrieve (the "limit"). |
||
637 | * Returns NULL if {@link setMaxResults} was not applied to this query builder. |
||
638 | * |
||
639 | * @return int|null Maximum number of results. |
||
640 | */ |
||
641 | 2 | public function getMaxResults() |
|
642 | { |
||
643 | 2 | return $this->maxResults; |
|
644 | } |
||
645 | |||
646 | /** |
||
647 | * Either appends to or replaces a single, generic query part. |
||
648 | * |
||
649 | * The available parts are: 'select', 'from', 'join', 'set', 'where', |
||
650 | * 'groupBy', 'having' and 'orderBy'. |
||
651 | * |
||
652 | * @param string $dqlPartName The DQL part name. |
||
653 | * @param object|mixed[] $dqlPart An Expr object. |
||
654 | * @param bool $append Whether to append (true) or replace (false). |
||
655 | * |
||
656 | * @return self |
||
657 | */ |
||
658 | 123 | public function add($dqlPartName, $dqlPart, $append = false) |
|
659 | { |
||
660 | 123 | if ($append && ($dqlPartName === 'where' || $dqlPartName === 'having')) { |
|
661 | 1 | throw new \InvalidArgumentException( |
|
662 | "Using \$append = true does not have an effect with 'where' or 'having' " . |
||
663 | 1 | 'parts. See QueryBuilder#andWhere() for an example for correct usage.' |
|
664 | ); |
||
665 | } |
||
666 | |||
667 | 123 | $isMultiple = is_array($this->dqlParts[$dqlPartName]) |
|
668 | 123 | && ! ($dqlPartName === 'join' && ! $append); |
|
669 | |||
670 | // Allow adding any part retrieved from self::getDQLParts(). |
||
671 | 123 | if (is_array($dqlPart) && $dqlPartName !== 'join') { |
|
672 | 1 | $dqlPart = reset($dqlPart); |
|
673 | } |
||
674 | |||
675 | // This is introduced for backwards compatibility reasons. |
||
676 | // TODO: Remove for 3.0 |
||
677 | 123 | if ($dqlPartName === 'join') { |
|
678 | 31 | $newDqlPart = []; |
|
679 | |||
680 | 31 | foreach ($dqlPart as $k => $v) { |
|
681 | 31 | $k = is_numeric($k) ? $this->getRootAlias() : $k; |
|
682 | |||
683 | 31 | $newDqlPart[$k] = $v; |
|
684 | } |
||
685 | |||
686 | 31 | $dqlPart = $newDqlPart; |
|
687 | } |
||
688 | |||
689 | 123 | if ($append && $isMultiple) { |
|
690 | 117 | if (is_array($dqlPart)) { |
|
691 | 31 | $key = key($dqlPart); |
|
692 | |||
693 | 31 | $this->dqlParts[$dqlPartName][$key][] = $dqlPart[$key]; |
|
694 | } else { |
||
695 | 117 | $this->dqlParts[$dqlPartName][] = $dqlPart; |
|
696 | } |
||
697 | } else { |
||
698 | 121 | $this->dqlParts[$dqlPartName] = ($isMultiple) ? [$dqlPart] : $dqlPart; |
|
699 | } |
||
700 | |||
701 | 123 | $this->state = self::STATE_DIRTY; |
|
702 | |||
703 | 123 | return $this; |
|
704 | } |
||
705 | |||
706 | /** |
||
707 | * Specifies an item that is to be returned in the query result. |
||
708 | * Replaces any previously specified selections, if any. |
||
709 | * |
||
710 | * <code> |
||
711 | * $qb = $em->createQueryBuilder() |
||
712 | * ->select('u', 'p') |
||
713 | * ->from('User', 'u') |
||
714 | * ->leftJoin('u.Phonenumbers', 'p'); |
||
715 | * </code> |
||
716 | * |
||
717 | * @param mixed $select The selection expressions. |
||
718 | * |
||
719 | * @return self |
||
720 | */ |
||
721 | 116 | public function select($select = null) |
|
722 | { |
||
723 | 116 | $this->type = self::SELECT; |
|
724 | |||
725 | 116 | if (empty($select)) { |
|
726 | 1 | return $this; |
|
727 | } |
||
728 | |||
729 | 115 | $selects = is_array($select) ? $select : func_get_args(); |
|
730 | |||
731 | 115 | return $this->add('select', new Expr\Select($selects), false); |
|
732 | } |
||
733 | |||
734 | /** |
||
735 | * Adds a DISTINCT flag to this query. |
||
736 | * |
||
737 | * <code> |
||
738 | * $qb = $em->createQueryBuilder() |
||
739 | * ->select('u') |
||
740 | * ->distinct() |
||
741 | * ->from('User', 'u'); |
||
742 | * </code> |
||
743 | * |
||
744 | * @param bool $flag |
||
745 | * |
||
746 | * @return self |
||
747 | */ |
||
748 | 1 | public function distinct($flag = true) |
|
749 | { |
||
750 | 1 | $this->dqlParts['distinct'] = (bool) $flag; |
|
751 | |||
752 | 1 | return $this; |
|
753 | } |
||
754 | |||
755 | /** |
||
756 | * Adds an item that is to be returned in the query result. |
||
757 | * |
||
758 | * <code> |
||
759 | * $qb = $em->createQueryBuilder() |
||
760 | * ->select('u') |
||
761 | * ->addSelect('p') |
||
762 | * ->from('User', 'u') |
||
763 | * ->leftJoin('u.Phonenumbers', 'p'); |
||
764 | * </code> |
||
765 | * |
||
766 | * @param mixed $select The selection expression. |
||
767 | * |
||
768 | * @return self |
||
769 | */ |
||
770 | 1 | public function addSelect($select = null) |
|
771 | { |
||
772 | 1 | $this->type = self::SELECT; |
|
773 | |||
774 | 1 | if (empty($select)) { |
|
775 | return $this; |
||
776 | } |
||
777 | |||
778 | 1 | $selects = is_array($select) ? $select : func_get_args(); |
|
779 | |||
780 | 1 | return $this->add('select', new Expr\Select($selects), true); |
|
781 | } |
||
782 | |||
783 | /** |
||
784 | * Turns the query being built into a bulk delete query that ranges over |
||
785 | * a certain entity type. |
||
786 | * |
||
787 | * <code> |
||
788 | * $qb = $em->createQueryBuilder() |
||
789 | * ->delete('User', 'u') |
||
790 | * ->where('u.id = :user_id') |
||
791 | * ->setParameter('user_id', 1); |
||
792 | * </code> |
||
793 | * |
||
794 | * @param string $delete The class/type whose instances are subject to the deletion. |
||
795 | * @param string $alias The class/type alias used in the constructed query. |
||
796 | * |
||
797 | * @return self |
||
798 | */ |
||
799 | 4 | public function delete($delete = null, $alias = null) |
|
800 | { |
||
801 | 4 | $this->type = self::DELETE; |
|
802 | |||
803 | 4 | if (! $delete) { |
|
804 | 1 | return $this; |
|
805 | } |
||
806 | |||
807 | 3 | return $this->add('from', new Expr\From($delete, $alias)); |
|
808 | } |
||
809 | |||
810 | /** |
||
811 | * Turns the query being built into a bulk update query that ranges over |
||
812 | * a certain entity type. |
||
813 | * |
||
814 | * <code> |
||
815 | * $qb = $em->createQueryBuilder() |
||
816 | * ->update('User', 'u') |
||
817 | * ->set('u.password', '?1') |
||
818 | * ->where('u.id = ?2'); |
||
819 | * </code> |
||
820 | * |
||
821 | * @param string $update The class/type whose instances are subject to the update. |
||
822 | * @param string $alias The class/type alias used in the constructed query. |
||
823 | * |
||
824 | * @return self |
||
825 | */ |
||
826 | 4 | public function update($update = null, $alias = null) |
|
827 | { |
||
828 | 4 | $this->type = self::UPDATE; |
|
829 | |||
830 | 4 | if (! $update) { |
|
831 | 1 | return $this; |
|
832 | } |
||
833 | |||
834 | 3 | return $this->add('from', new Expr\From($update, $alias)); |
|
835 | } |
||
836 | |||
837 | /** |
||
838 | * Creates and adds a query root corresponding to the entity identified by the given alias, |
||
839 | * forming a cartesian product with any existing query roots. |
||
840 | * |
||
841 | * <code> |
||
842 | * $qb = $em->createQueryBuilder() |
||
843 | * ->select('u') |
||
844 | * ->from('User', 'u'); |
||
845 | * </code> |
||
846 | * |
||
847 | * @param string $from The class name. |
||
848 | * @param string $alias The alias of the class. |
||
849 | * @param string $indexBy The index for the from. |
||
850 | * |
||
851 | * @return self |
||
852 | */ |
||
853 | 115 | public function from($from, $alias, $indexBy = null) |
|
854 | { |
||
855 | 115 | return $this->add('from', new Expr\From($from, $alias, $indexBy), true); |
|
856 | } |
||
857 | |||
858 | /** |
||
859 | * Updates a query root corresponding to an entity setting its index by. This method is intended to be used with |
||
860 | * EntityRepository->createQueryBuilder(), which creates the initial FROM clause and do not allow you to update it |
||
861 | * setting an index by. |
||
862 | * |
||
863 | * <code> |
||
864 | * $qb = $userRepository->createQueryBuilder('u') |
||
865 | * ->indexBy('u', 'u.id'); |
||
866 | * |
||
867 | * // Is equivalent to... |
||
868 | * |
||
869 | * $qb = $em->createQueryBuilder() |
||
870 | * ->select('u') |
||
871 | * ->from('User', 'u', 'u.id'); |
||
872 | * </code> |
||
873 | * |
||
874 | * @param string $alias The root alias of the class. |
||
875 | * @param string $indexBy The index for the from. |
||
876 | * |
||
877 | * @return self |
||
878 | * |
||
879 | * @throws Query\QueryException |
||
880 | */ |
||
881 | 2 | public function indexBy($alias, $indexBy) |
|
882 | { |
||
883 | 2 | $rootAliases = $this->getRootAliases(); |
|
884 | |||
885 | 2 | if (! in_array($alias, $rootAliases)) { |
|
886 | throw new Query\QueryException( |
||
887 | sprintf('Specified root alias %s must be set before invoking indexBy().', $alias) |
||
888 | ); |
||
889 | } |
||
890 | |||
891 | 2 | foreach ($this->dqlParts['from'] as &$fromClause) { |
|
892 | /* @var Expr\From $fromClause */ |
||
893 | 2 | if ($fromClause->getAlias() !== $alias) { |
|
894 | 1 | continue; |
|
895 | } |
||
896 | |||
897 | 2 | $fromClause = new Expr\From($fromClause->getFrom(), $fromClause->getAlias(), $indexBy); |
|
898 | } |
||
899 | |||
900 | 2 | return $this; |
|
901 | } |
||
902 | |||
903 | /** |
||
904 | * Creates and adds a join over an entity association to the query. |
||
905 | * |
||
906 | * The entities in the joined association will be fetched as part of the query |
||
907 | * result if the alias used for the joined association is placed in the select |
||
908 | * expressions. |
||
909 | * |
||
910 | * <code> |
||
911 | * $qb = $em->createQueryBuilder() |
||
912 | * ->select('u') |
||
913 | * ->from('User', 'u') |
||
914 | * ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); |
||
915 | * </code> |
||
916 | * |
||
917 | * @param string $join The relationship to join. |
||
918 | * @param string $alias The alias of the join. |
||
919 | * @param string|null $conditionType The condition type constant. Either ON or WITH. |
||
920 | * @param string|null $condition The condition for the join. |
||
921 | * @param string|null $indexBy The index for the join. |
||
922 | * |
||
923 | * @return self |
||
924 | */ |
||
925 | 8 | public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null) |
|
926 | { |
||
927 | 8 | return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy); |
|
928 | } |
||
929 | |||
930 | /** |
||
931 | * Creates and adds a join over an entity association to the query. |
||
932 | * |
||
933 | * The entities in the joined association will be fetched as part of the query |
||
934 | * result if the alias used for the joined association is placed in the select |
||
935 | * expressions. |
||
936 | * |
||
937 | * [php] |
||
938 | * $qb = $em->createQueryBuilder() |
||
939 | * ->select('u') |
||
940 | * ->from('User', 'u') |
||
941 | * ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); |
||
942 | * |
||
943 | * @param string $join The relationship to join. |
||
944 | * @param string $alias The alias of the join. |
||
945 | * @param string|null $conditionType The condition type constant. Either ON or WITH. |
||
946 | * @param string|null $condition The condition for the join. |
||
947 | * @param string|null $indexBy The index for the join. |
||
948 | * |
||
949 | * @return self |
||
950 | */ |
||
951 | 16 | public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) |
|
952 | { |
||
953 | 16 | $hasParentAlias = strpos($join, '.'); |
|
954 | 16 | $parentAlias = substr($join, 0, $hasParentAlias === false ? 0 : $hasParentAlias); |
|
955 | 16 | $rootAlias = $this->findRootAlias($alias, $parentAlias); |
|
956 | 16 | $join = new Expr\Join( |
|
957 | 16 | Expr\Join::INNER_JOIN, |
|
958 | 16 | $join, |
|
959 | 16 | $alias, |
|
960 | 16 | $conditionType, |
|
961 | 16 | $condition, |
|
962 | 16 | $indexBy |
|
963 | ); |
||
964 | |||
965 | 16 | return $this->add('join', [$rootAlias => $join], true); |
|
966 | } |
||
967 | |||
968 | /** |
||
969 | * Creates and adds a left join over an entity association to the query. |
||
970 | * |
||
971 | * The entities in the joined association will be fetched as part of the query |
||
972 | * result if the alias used for the joined association is placed in the select |
||
973 | * expressions. |
||
974 | * |
||
975 | * <code> |
||
976 | * $qb = $em->createQueryBuilder() |
||
977 | * ->select('u') |
||
978 | * ->from('User', 'u') |
||
979 | * ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); |
||
980 | * </code> |
||
981 | * |
||
982 | * @param string $join The relationship to join. |
||
983 | * @param string $alias The alias of the join. |
||
984 | * @param string|null $conditionType The condition type constant. Either ON or WITH. |
||
985 | * @param string|null $condition The condition for the join. |
||
986 | * @param string|null $indexBy The index for the join. |
||
987 | * |
||
988 | * @return self |
||
989 | */ |
||
990 | 15 | public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) |
|
991 | { |
||
992 | 15 | $hasParentAlias = strpos($join, '.'); |
|
993 | 15 | $parentAlias = substr($join, 0, $hasParentAlias === false ? 0 : $hasParentAlias); |
|
994 | 15 | $rootAlias = $this->findRootAlias($alias, $parentAlias); |
|
995 | 15 | $join = new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy); |
|
996 | |||
997 | 15 | return $this->add('join', [$rootAlias => $join], true); |
|
998 | } |
||
999 | |||
1000 | /** |
||
1001 | * Sets a new value for a field in a bulk update query. |
||
1002 | * |
||
1003 | * <code> |
||
1004 | * $qb = $em->createQueryBuilder() |
||
1005 | * ->update('User', 'u') |
||
1006 | * ->set('u.password', '?1') |
||
1007 | * ->where('u.id = ?2'); |
||
1008 | * </code> |
||
1009 | * |
||
1010 | * @param string $key The key/field to set. |
||
1011 | * @param string $value The value, expression, placeholder, etc. |
||
1012 | * |
||
1013 | * @return self |
||
1014 | */ |
||
1015 | 3 | public function set($key, $value) |
|
1016 | { |
||
1017 | 3 | return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true); |
|
1018 | } |
||
1019 | |||
1020 | /** |
||
1021 | * Specifies one or more restrictions to the query result. |
||
1022 | * Replaces any previously specified restrictions, if any. |
||
1023 | * |
||
1024 | * <code> |
||
1025 | * $qb = $em->createQueryBuilder() |
||
1026 | * ->select('u') |
||
1027 | * ->from('User', 'u') |
||
1028 | * ->where('u.id = ?'); |
||
1029 | * |
||
1030 | * // You can optionally programmatically build and/or expressions |
||
1031 | * $qb = $em->createQueryBuilder(); |
||
1032 | * |
||
1033 | * $or = $qb->expr()->orX(); |
||
1034 | * $or->add($qb->expr()->eq('u.id', 1)); |
||
1035 | * $or->add($qb->expr()->eq('u.id', 2)); |
||
1036 | * |
||
1037 | * $qb->update('User', 'u') |
||
1038 | * ->set('u.password', '?') |
||
1039 | * ->where($or); |
||
1040 | * </code> |
||
1041 | * |
||
1042 | * @param mixed $predicates The restriction predicates. |
||
1043 | */ |
||
1044 | 47 | public function where($predicates) |
|
1045 | { |
||
1046 | 47 | if (! (func_num_args() === 1 && $predicates instanceof Expr\Composite)) { |
|
1047 | 44 | $predicates = new Expr\Andx(func_get_args()); |
|
1048 | } |
||
1049 | |||
1050 | 47 | return $this->add('where', $predicates); |
|
1051 | } |
||
1052 | |||
1053 | /** |
||
1054 | * Adds one or more restrictions to the query results, forming a logical |
||
1055 | * conjunction with any previously specified restrictions. |
||
1056 | * |
||
1057 | * <code> |
||
1058 | * $qb = $em->createQueryBuilder() |
||
1059 | * ->select('u') |
||
1060 | * ->from('User', 'u') |
||
1061 | * ->where('u.username LIKE ?') |
||
1062 | * ->andWhere('u.is_active = 1'); |
||
1063 | * </code> |
||
1064 | * |
||
1065 | * @//param mixed $where The query restrictions. |
||
1066 | * |
||
1067 | * @see where() |
||
1068 | */ |
||
1069 | 23 | public function andWhere() |
|
1070 | { |
||
1071 | 23 | $args = func_get_args(); |
|
1072 | 23 | $where = $this->getDQLPart('where'); |
|
1073 | |||
1074 | 23 | if ($where instanceof Expr\Andx) { |
|
1075 | 11 | $where->addMultiple($args); |
|
1076 | } else { |
||
1077 | 15 | array_unshift($args, $where); |
|
1078 | 15 | $where = new Expr\Andx($args); |
|
1079 | } |
||
1080 | |||
1081 | 23 | return $this->add('where', $where); |
|
1082 | } |
||
1083 | |||
1084 | /** |
||
1085 | * Adds one or more restrictions to the query results, forming a logical |
||
1086 | * disjunction with any previously specified restrictions. |
||
1087 | * |
||
1088 | * <code> |
||
1089 | * $qb = $em->createQueryBuilder() |
||
1090 | * ->select('u') |
||
1091 | * ->from('User', 'u') |
||
1092 | * ->where('u.id = 1') |
||
1093 | * ->orWhere('u.id = 2'); |
||
1094 | * </code> |
||
1095 | * |
||
1096 | * @//param mixed $where The WHERE statement. |
||
1097 | * |
||
1098 | * @see where() |
||
1099 | */ |
||
1100 | 5 | public function orWhere() |
|
1101 | { |
||
1102 | 5 | $args = func_get_args(); |
|
1103 | 5 | $where = $this->getDQLPart('where'); |
|
1104 | |||
1105 | 5 | if ($where instanceof Expr\Orx) { |
|
1106 | $where->addMultiple($args); |
||
1107 | } else { |
||
1108 | 5 | array_unshift($args, $where); |
|
1109 | 5 | $where = new Expr\Orx($args); |
|
1110 | } |
||
1111 | |||
1112 | 5 | return $this->add('where', $where); |
|
1113 | } |
||
1114 | |||
1115 | /** |
||
1116 | * Specifies a grouping over the results of the query. |
||
1117 | * Replaces any previously specified groupings, if any. |
||
1118 | * |
||
1119 | * <code> |
||
1120 | * $qb = $em->createQueryBuilder() |
||
1121 | * ->select('u') |
||
1122 | * ->from('User', 'u') |
||
1123 | * ->groupBy('u.id'); |
||
1124 | * </code> |
||
1125 | * |
||
1126 | * @param string $groupBy The grouping expression. |
||
1127 | * |
||
1128 | * @return self |
||
1129 | */ |
||
1130 | 7 | public function groupBy($groupBy) |
|
1131 | { |
||
1132 | 7 | return $this->add('groupBy', new Expr\GroupBy(func_get_args())); |
|
1133 | } |
||
1134 | |||
1135 | /** |
||
1136 | * Adds a grouping expression to the query. |
||
1137 | * |
||
1138 | * <code> |
||
1139 | * $qb = $em->createQueryBuilder() |
||
1140 | * ->select('u') |
||
1141 | * ->from('User', 'u') |
||
1142 | * ->groupBy('u.lastLogin') |
||
1143 | * ->addGroupBy('u.createdAt'); |
||
1144 | * </code> |
||
1145 | * |
||
1146 | * @param string $groupBy The grouping expression. |
||
1147 | * |
||
1148 | * @return self |
||
1149 | */ |
||
1150 | 1 | public function addGroupBy($groupBy) |
|
1151 | { |
||
1152 | 1 | return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true); |
|
1153 | } |
||
1154 | |||
1155 | /** |
||
1156 | * Specifies a restriction over the groups of the query. |
||
1157 | * Replaces any previous having restrictions, if any. |
||
1158 | * |
||
1159 | * @param mixed $having The restriction over the groups. |
||
1160 | * |
||
1161 | * @return self |
||
1162 | */ |
||
1163 | 3 | public function having($having) |
|
1164 | { |
||
1165 | 3 | if (! (func_num_args() === 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) { |
|
1166 | 3 | $having = new Expr\Andx(func_get_args()); |
|
1167 | } |
||
1168 | |||
1169 | 3 | return $this->add('having', $having); |
|
1170 | } |
||
1171 | |||
1172 | /** |
||
1173 | * Adds a restriction over the groups of the query, forming a logical |
||
1174 | * conjunction with any existing having restrictions. |
||
1175 | * |
||
1176 | * @param mixed $having The restriction to append. |
||
1177 | * |
||
1178 | * @return self |
||
1179 | */ |
||
1180 | 2 | public function andHaving($having) |
|
1181 | { |
||
1182 | 2 | $args = func_get_args(); |
|
1183 | 2 | $having = $this->getDQLPart('having'); |
|
1184 | |||
1185 | 2 | if ($having instanceof Expr\Andx) { |
|
1186 | 2 | $having->addMultiple($args); |
|
1187 | } else { |
||
1188 | array_unshift($args, $having); |
||
1189 | $having = new Expr\Andx($args); |
||
1190 | } |
||
1191 | |||
1192 | 2 | return $this->add('having', $having); |
|
1193 | } |
||
1194 | |||
1195 | /** |
||
1196 | * Adds a restriction over the groups of the query, forming a logical |
||
1197 | * disjunction with any existing having restrictions. |
||
1198 | * |
||
1199 | * @param mixed $having The restriction to add. |
||
1200 | * |
||
1201 | * @return self |
||
1202 | */ |
||
1203 | 1 | public function orHaving($having) |
|
1204 | { |
||
1205 | 1 | $args = func_get_args(); |
|
1206 | 1 | $having = $this->getDQLPart('having'); |
|
1207 | |||
1208 | 1 | if ($having instanceof Expr\Orx) { |
|
1209 | $having->addMultiple($args); |
||
1210 | } else { |
||
1211 | 1 | array_unshift($args, $having); |
|
1212 | 1 | $having = new Expr\Orx($args); |
|
1213 | } |
||
1214 | |||
1215 | 1 | return $this->add('having', $having); |
|
1216 | } |
||
1217 | |||
1218 | /** |
||
1219 | * Specifies an ordering for the query results. |
||
1220 | * Replaces any previously specified orderings, if any. |
||
1221 | * |
||
1222 | * @param string|Expr\OrderBy $sort The ordering expression. |
||
1223 | * @param string $order The ordering direction. |
||
1224 | * |
||
1225 | * @return self |
||
1226 | */ |
||
1227 | 10 | public function orderBy($sort, $order = null) |
|
1228 | { |
||
1229 | 10 | $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); |
|
1230 | |||
1231 | 10 | return $this->add('orderBy', $orderBy); |
|
1232 | } |
||
1233 | |||
1234 | /** |
||
1235 | * Adds an ordering to the query results. |
||
1236 | * |
||
1237 | * @param string|Expr\OrderBy $sort The ordering expression. |
||
1238 | * @param string $order The ordering direction. |
||
1239 | * |
||
1240 | * @return self |
||
1241 | */ |
||
1242 | 4 | public function addOrderBy($sort, $order = null) |
|
1243 | { |
||
1244 | 4 | $orderBy = ($sort instanceof Expr\OrderBy) ? $sort : new Expr\OrderBy($sort, $order); |
|
1245 | |||
1246 | 4 | return $this->add('orderBy', $orderBy, true); |
|
1247 | } |
||
1248 | |||
1249 | /** |
||
1250 | * Adds criteria to the query. |
||
1251 | * |
||
1252 | * Adds where expressions with AND operator. |
||
1253 | * Adds orderings. |
||
1254 | * Overrides firstResult and maxResults if they're set. |
||
1255 | * |
||
1256 | * |
||
1257 | * @return self |
||
1258 | * |
||
1259 | * @throws Query\QueryException |
||
1260 | */ |
||
1261 | 13 | public function addCriteria(Criteria $criteria) |
|
1262 | { |
||
1263 | 13 | $allAliases = $this->getAllAliases(); |
|
1264 | 13 | if (! isset($allAliases[0])) { |
|
1265 | throw new Query\QueryException('No aliases are set before invoking addCriteria().'); |
||
1266 | } |
||
1267 | |||
1268 | 13 | $visitor = new QueryExpressionVisitor($this->getAllAliases()); |
|
1269 | 13 | $whereExpression = $criteria->getWhereExpression(); |
|
1270 | |||
1271 | 13 | if ($whereExpression) { |
|
1272 | 9 | $this->andWhere($visitor->dispatch($whereExpression)); |
|
1273 | 9 | foreach ($visitor->getParameters() as $parameter) { |
|
1274 | 9 | $this->parameters->add($parameter); |
|
1275 | } |
||
1276 | } |
||
1277 | |||
1278 | 13 | if ($criteria->getOrderings()) { |
|
1279 | 2 | foreach ($criteria->getOrderings() as $sort => $order) { |
|
1280 | 2 | $hasValidAlias = false; |
|
1281 | 2 | foreach ($allAliases as $alias) { |
|
1282 | 2 | if (strpos($sort . '.', $alias . '.') === 0) { |
|
1283 | 1 | $hasValidAlias = true; |
|
1284 | 2 | break; |
|
1285 | } |
||
1286 | } |
||
1287 | |||
1288 | 2 | if (! $hasValidAlias) { |
|
1289 | 1 | $sort = $allAliases[0] . '.' . $sort; |
|
1290 | } |
||
1291 | |||
1292 | 2 | $this->addOrderBy($sort, $order); |
|
1293 | } |
||
1294 | } |
||
1295 | |||
1296 | 13 | $firstResult = $criteria->getFirstResult(); |
|
1297 | 13 | $maxResults = $criteria->getMaxResults(); |
|
1298 | |||
1299 | // Overwrite limits only if they was set in criteria |
||
1300 | 13 | if ($firstResult !== null) { |
|
1301 | 1 | $this->setFirstResult($firstResult); |
|
1302 | } |
||
1303 | 13 | if ($maxResults !== null) { |
|
1304 | 1 | $this->setMaxResults($maxResults); |
|
1305 | } |
||
1306 | |||
1307 | 13 | return $this; |
|
1308 | } |
||
1309 | |||
1310 | /** |
||
1311 | * Gets a query part by its name. |
||
1312 | * |
||
1313 | * @return mixed $queryPart |
||
1314 | * |
||
1315 | * @todo Rename: getQueryPart (or remove?) |
||
1316 | */ |
||
1317 | 105 | public function getDQLPart($queryPartName) |
|
1318 | { |
||
1319 | 105 | return $this->dqlParts[$queryPartName]; |
|
1320 | } |
||
1321 | |||
1322 | /** |
||
1323 | * Gets all query parts. |
||
1324 | * |
||
1325 | * @return mixed[] $dqlParts |
||
1326 | * |
||
1327 | * @todo Rename: getQueryParts (or remove?) |
||
1328 | */ |
||
1329 | 1 | public function getDQLParts() |
|
1330 | { |
||
1331 | 1 | return $this->dqlParts; |
|
1332 | } |
||
1333 | |||
1334 | /** |
||
1335 | * @return string |
||
1336 | */ |
||
1337 | 1 | private function getDQLForDelete() |
|
1338 | { |
||
1339 | return 'DELETE' |
||
1340 | 1 | . $this->getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', ']) |
|
1341 | 1 | . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE ']) |
|
1342 | 1 | . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']); |
|
1343 | } |
||
1344 | |||
1345 | /** |
||
1346 | * @return string |
||
1347 | */ |
||
1348 | 3 | private function getDQLForUpdate() |
|
1349 | { |
||
1350 | return 'UPDATE' |
||
1351 | 3 | . $this->getReducedDQLQueryPart('from', ['pre' => ' ', 'separator' => ', ']) |
|
1352 | 3 | . $this->getReducedDQLQueryPart('set', ['pre' => ' SET ', 'separator' => ', ']) |
|
1353 | 3 | . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE ']) |
|
1354 | 3 | . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']); |
|
1355 | } |
||
1356 | |||
1357 | /** |
||
1358 | * @return string |
||
1359 | */ |
||
1360 | 84 | private function getDQLForSelect() |
|
1361 | { |
||
1362 | $dql = 'SELECT' |
||
1363 | 84 | . ($this->dqlParts['distinct']===true ? ' DISTINCT' : '') |
|
1364 | 84 | . $this->getReducedDQLQueryPart('select', ['pre' => ' ', 'separator' => ', ']); |
|
1365 | |||
1366 | 84 | $fromParts = $this->getDQLPart('from'); |
|
1367 | 84 | $joinParts = $this->getDQLPart('join'); |
|
1368 | 84 | $fromClauses = []; |
|
1369 | |||
1370 | // Loop through all FROM clauses |
||
1371 | 84 | if (! empty($fromParts)) { |
|
1372 | 83 | $dql .= ' FROM '; |
|
1373 | |||
1374 | 83 | foreach ($fromParts as $from) { |
|
1375 | 83 | $fromClause = (string) $from; |
|
1376 | |||
1377 | 83 | if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) { |
|
1378 | 26 | foreach ($joinParts[$from->getAlias()] as $join) { |
|
1379 | 26 | $fromClause .= ' ' . ((string) $join); |
|
1380 | } |
||
1381 | } |
||
1382 | |||
1383 | 83 | $fromClauses[] = $fromClause; |
|
1384 | } |
||
1385 | } |
||
1386 | |||
1387 | 84 | $dql .= implode(', ', $fromClauses) |
|
1388 | 84 | . $this->getReducedDQLQueryPart('where', ['pre' => ' WHERE ']) |
|
1389 | 84 | . $this->getReducedDQLQueryPart('groupBy', ['pre' => ' GROUP BY ', 'separator' => ', ']) |
|
1390 | 84 | . $this->getReducedDQLQueryPart('having', ['pre' => ' HAVING ']) |
|
1391 | 84 | . $this->getReducedDQLQueryPart('orderBy', ['pre' => ' ORDER BY ', 'separator' => ', ']); |
|
1392 | |||
1393 | 84 | return $dql; |
|
1394 | } |
||
1395 | |||
1396 | /** |
||
1397 | * @param string $queryPartName |
||
1398 | * @param mixed[] $options |
||
1399 | * |
||
1400 | * @return string |
||
1401 | */ |
||
1402 | 87 | private function getReducedDQLQueryPart($queryPartName, $options = []) |
|
1403 | { |
||
1404 | 87 | $queryPart = $this->getDQLPart($queryPartName); |
|
1405 | |||
1406 | 87 | if (empty($queryPart)) { |
|
1407 | 87 | return $options['empty'] ?? ''; |
|
1408 | } |
||
1409 | |||
1410 | 87 | return ($options['pre'] ?? '') |
|
1411 | 87 | . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart) |
|
1412 | 87 | . ($options['post'] ?? ''); |
|
1413 | } |
||
1414 | |||
1415 | /** |
||
1416 | * Resets DQL parts. |
||
1417 | * |
||
1418 | * @param string[]|null $parts |
||
1419 | */ |
||
1420 | 2 | public function resetDQLParts($parts = null) |
|
1421 | { |
||
1422 | 2 | if ($parts === null) { |
|
1423 | 1 | $parts = array_keys($this->dqlParts); |
|
1424 | } |
||
1425 | |||
1426 | 2 | foreach ($parts as $part) { |
|
1427 | 2 | $this->resetDQLPart($part); |
|
1428 | } |
||
1429 | |||
1430 | 2 | return $this; |
|
1431 | } |
||
1432 | |||
1433 | /** |
||
1434 | * Resets single DQL part. |
||
1435 | * |
||
1436 | * @param string $part |
||
1437 | * |
||
1438 | * @return self |
||
1439 | */ |
||
1440 | 3 | public function resetDQLPart($part) |
|
1441 | { |
||
1442 | 3 | $this->dqlParts[$part] = is_array($this->dqlParts[$part]) ? [] : null; |
|
1443 | 3 | $this->state = self::STATE_DIRTY; |
|
1444 | |||
1445 | 3 | return $this; |
|
1446 | } |
||
1447 | |||
1448 | /** |
||
1449 | * Gets a string representation of this QueryBuilder which corresponds to |
||
1450 | * the final DQL query being constructed. |
||
1451 | * |
||
1452 | * @return string The string representation of this QueryBuilder. |
||
1453 | */ |
||
1454 | 5 | public function __toString() |
|
1455 | { |
||
1456 | 5 | return $this->getDQL(); |
|
1457 | } |
||
1458 | |||
1459 | /** |
||
1460 | * Deep clones all expression objects in the DQL parts. |
||
1461 | * |
||
1462 | */ |
||
1463 | 3 | public function __clone() |
|
1464 | { |
||
1465 | 3 | foreach ($this->dqlParts as $part => $elements) { |
|
1466 | 3 | if (is_array($this->dqlParts[$part])) { |
|
1467 | 3 | foreach ($this->dqlParts[$part] as $idx => $element) { |
|
1468 | 2 | if (is_object($element)) { |
|
1469 | 3 | $this->dqlParts[$part][$idx] = clone $element; |
|
1470 | } |
||
1471 | } |
||
1472 | 3 | } elseif (is_object($elements)) { |
|
1473 | 3 | $this->dqlParts[$part] = clone $elements; |
|
1474 | } |
||
1475 | } |
||
1476 | |||
1477 | 3 | $parameters = []; |
|
1478 | |||
1479 | 3 | foreach ($this->parameters as $parameter) { |
|
1480 | 1 | $parameters[] = clone $parameter; |
|
1481 | } |
||
1482 | |||
1483 | 3 | $this->parameters = new ArrayCollection($parameters); |
|
1484 | 3 | } |
|
1485 | } |
||
1486 |
This function has been deprecated. The supplier of the function has supplied an explanatory message.
The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.