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 LGPL. For more information, see |
---|
17 | * <http://www.doctrine-project.org>. |
---|
18 | */ |
---|
19 | |
---|
20 | namespace Doctrine\ORM; |
---|
21 | |
---|
22 | use Doctrine\ORM\Query\Expr; |
---|
23 | |
---|
24 | /** |
---|
25 | * This class is responsible for building DQL query strings via an object oriented |
---|
26 | * PHP interface. |
---|
27 | * |
---|
28 | * @since 2.0 |
---|
29 | * @author Guilherme Blanco <guilhermeblanco@hotmail.com> |
---|
30 | * @author Jonathan Wage <jonwage@gmail.com> |
---|
31 | * @author Roman Borschel <roman@code-factory.org> |
---|
32 | */ |
---|
33 | class QueryBuilder |
---|
34 | { |
---|
35 | /* The query types. */ |
---|
36 | const SELECT = 0; |
---|
37 | const DELETE = 1; |
---|
38 | const UPDATE = 2; |
---|
39 | |
---|
40 | /** The builder states. */ |
---|
41 | const STATE_DIRTY = 0; |
---|
42 | const STATE_CLEAN = 1; |
---|
43 | |
---|
44 | /** |
---|
45 | * @var EntityManager The EntityManager used by this QueryBuilder. |
---|
46 | */ |
---|
47 | private $_em; |
---|
48 | |
---|
49 | /** |
---|
50 | * @var array The array of DQL parts collected. |
---|
51 | */ |
---|
52 | private $_dqlParts = array( |
---|
53 | 'distinct' => false, |
---|
54 | 'select' => array(), |
---|
55 | 'from' => array(), |
---|
56 | 'join' => array(), |
---|
57 | 'set' => array(), |
---|
58 | 'where' => null, |
---|
59 | 'groupBy' => array(), |
---|
60 | 'having' => null, |
---|
61 | 'orderBy' => array() |
---|
62 | ); |
---|
63 | |
---|
64 | /** |
---|
65 | * @var integer The type of query this is. Can be select, update or delete. |
---|
66 | */ |
---|
67 | private $_type = self::SELECT; |
---|
68 | |
---|
69 | /** |
---|
70 | * @var integer The state of the query object. Can be dirty or clean. |
---|
71 | */ |
---|
72 | private $_state = self::STATE_CLEAN; |
---|
73 | |
---|
74 | /** |
---|
75 | * @var string The complete DQL string for this query. |
---|
76 | */ |
---|
77 | private $_dql; |
---|
78 | |
---|
79 | /** |
---|
80 | * @var array The query parameters. |
---|
81 | */ |
---|
82 | private $_params = array(); |
---|
83 | |
---|
84 | /** |
---|
85 | * @var array The parameter type map of this query. |
---|
86 | */ |
---|
87 | private $_paramTypes = array(); |
---|
88 | |
---|
89 | /** |
---|
90 | * @var integer The index of the first result to retrieve. |
---|
91 | */ |
---|
92 | private $_firstResult = null; |
---|
93 | |
---|
94 | /** |
---|
95 | * @var integer The maximum number of results to retrieve. |
---|
96 | */ |
---|
97 | private $_maxResults = null; |
---|
98 | |
---|
99 | /** |
---|
100 | * Initializes a new <tt>QueryBuilder</tt> that uses the given <tt>EntityManager</tt>. |
---|
101 | * |
---|
102 | * @param EntityManager $em The EntityManager to use. |
---|
103 | */ |
---|
104 | public function __construct(EntityManager $em) |
---|
105 | { |
---|
106 | $this->_em = $em; |
---|
107 | } |
---|
108 | |
---|
109 | /** |
---|
110 | * Gets an ExpressionBuilder used for object-oriented construction of query expressions. |
---|
111 | * This producer method is intended for convenient inline usage. Example: |
---|
112 | * |
---|
113 | * <code> |
---|
114 | * $qb = $em->createQueryBuilder() |
---|
115 | * ->select('u') |
---|
116 | * ->from('User', 'u') |
---|
117 | * ->where($qb->expr()->eq('u.id', 1)); |
---|
118 | * </code> |
---|
119 | * |
---|
120 | * For more complex expression construction, consider storing the expression |
---|
121 | * builder object in a local variable. |
---|
122 | * |
---|
123 | * @return Query\Expr |
---|
124 | */ |
---|
125 | public function expr() |
---|
126 | { |
---|
127 | return $this->_em->getExpressionBuilder(); |
---|
128 | } |
---|
129 | |
---|
130 | /** |
---|
131 | * Get the type of the currently built query. |
---|
132 | * |
---|
133 | * @return integer |
---|
134 | */ |
---|
135 | public function getType() |
---|
136 | { |
---|
137 | return $this->_type; |
---|
138 | } |
---|
139 | |
---|
140 | /** |
---|
141 | * Get the associated EntityManager for this query builder. |
---|
142 | * |
---|
143 | * @return EntityManager |
---|
144 | */ |
---|
145 | public function getEntityManager() |
---|
146 | { |
---|
147 | return $this->_em; |
---|
148 | } |
---|
149 | |
---|
150 | /** |
---|
151 | * Get the state of this query builder instance. |
---|
152 | * |
---|
153 | * @return integer Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN. |
---|
154 | */ |
---|
155 | public function getState() |
---|
156 | { |
---|
157 | return $this->_state; |
---|
158 | } |
---|
159 | |
---|
160 | /** |
---|
161 | * Get the complete DQL string formed by the current specifications of this QueryBuilder. |
---|
162 | * |
---|
163 | * <code> |
---|
164 | * $qb = $em->createQueryBuilder() |
---|
165 | * ->select('u') |
---|
166 | * ->from('User', 'u') |
---|
167 | * echo $qb->getDql(); // SELECT u FROM User u |
---|
168 | * </code> |
---|
169 | * |
---|
170 | * @return string The DQL query string. |
---|
171 | */ |
---|
172 | public function getDQL() |
---|
173 | { |
---|
174 | if ($this->_dql !== null && $this->_state === self::STATE_CLEAN) { |
---|
175 | return $this->_dql; |
---|
176 | } |
---|
177 | |
---|
178 | $dql = ''; |
---|
179 | |
---|
180 | switch ($this->_type) { |
---|
181 | case self::DELETE: |
---|
182 | $dql = $this->_getDQLForDelete(); |
---|
183 | break; |
---|
184 | |
---|
185 | case self::UPDATE: |
---|
186 | $dql = $this->_getDQLForUpdate(); |
---|
187 | break; |
---|
188 | |
---|
189 | case self::SELECT: |
---|
190 | default: |
---|
191 | $dql = $this->_getDQLForSelect(); |
---|
192 | break; |
---|
193 | } |
---|
194 | |
---|
195 | $this->_state = self::STATE_CLEAN; |
---|
196 | $this->_dql = $dql; |
---|
197 | |
---|
198 | return $dql; |
---|
199 | } |
---|
200 | |
---|
201 | /** |
---|
202 | * Constructs a Query instance from the current specifications of the builder. |
---|
203 | * |
---|
204 | * <code> |
---|
205 | * $qb = $em->createQueryBuilder() |
---|
206 | * ->select('u') |
---|
207 | * ->from('User', 'u'); |
---|
208 | * $q = $qb->getQuery(); |
---|
209 | * $results = $q->execute(); |
---|
210 | * </code> |
---|
211 | * |
---|
212 | * @return Query |
---|
213 | */ |
---|
214 | public function getQuery() |
---|
215 | { |
---|
216 | return $this->_em->createQuery($this->getDQL()) |
---|
217 | ->setParameters($this->_params, $this->_paramTypes) |
---|
218 | ->setFirstResult($this->_firstResult) |
---|
219 | ->setMaxResults($this->_maxResults); |
---|
220 | } |
---|
221 | |
---|
222 | /** |
---|
223 | * Gets the FIRST root alias of the query. This is the first entity alias involved |
---|
224 | * in the construction of the query. |
---|
225 | * |
---|
226 | * <code> |
---|
227 | * $qb = $em->createQueryBuilder() |
---|
228 | * ->select('u') |
---|
229 | * ->from('User', 'u'); |
---|
230 | * |
---|
231 | * echo $qb->getRootAlias(); // u |
---|
232 | * </code> |
---|
233 | * |
---|
234 | * @deprecated Please use $qb->getRootAliases() instead. |
---|
235 | * @return string $rootAlias |
---|
236 | */ |
---|
237 | public function getRootAlias() |
---|
238 | { |
---|
239 | $aliases = $this->getRootAliases(); |
---|
240 | return $aliases[0]; |
---|
241 | } |
---|
242 | |
---|
243 | /** |
---|
244 | * Gets the root aliases of the query. This is the entity aliases involved |
---|
245 | * in the construction of the query. |
---|
246 | * |
---|
247 | * <code> |
---|
248 | * $qb = $em->createQueryBuilder() |
---|
249 | * ->select('u') |
---|
250 | * ->from('User', 'u'); |
---|
251 | * |
---|
252 | * $qb->getRootAliases(); // array('u') |
---|
253 | * </code> |
---|
254 | * |
---|
255 | * @return array $rootAliases |
---|
256 | */ |
---|
257 | public function getRootAliases() |
---|
258 | { |
---|
259 | $aliases = array(); |
---|
260 | |
---|
261 | foreach ($this->_dqlParts['from'] as &$fromClause) { |
---|
262 | if (is_string($fromClause)) { |
---|
263 | $spacePos = strrpos($fromClause, ' '); |
---|
264 | $from = substr($fromClause, 0, $spacePos); |
---|
265 | $alias = substr($fromClause, $spacePos + 1); |
---|
266 | |
---|
267 | $fromClause = new Query\Expr\From($from, $alias); |
---|
268 | } |
---|
269 | |
---|
270 | $aliases[] = $fromClause->getAlias(); |
---|
271 | } |
---|
272 | |
---|
273 | return $aliases; |
---|
274 | } |
---|
275 | |
---|
276 | /** |
---|
277 | * Gets the root entities of the query. This is the entity aliases involved |
---|
278 | * in the construction of the query. |
---|
279 | * |
---|
280 | * <code> |
---|
281 | * $qb = $em->createQueryBuilder() |
---|
282 | * ->select('u') |
---|
283 | * ->from('User', 'u'); |
---|
284 | * |
---|
285 | * $qb->getRootEntities(); // array('User') |
---|
286 | * </code> |
---|
287 | * |
---|
288 | * @return array $rootEntities |
---|
289 | */ |
---|
290 | public function getRootEntities() |
---|
291 | { |
---|
292 | $entities = array(); |
---|
293 | |
---|
294 | foreach ($this->_dqlParts['from'] as &$fromClause) { |
---|
295 | if (is_string($fromClause)) { |
---|
296 | $spacePos = strrpos($fromClause, ' '); |
---|
297 | $from = substr($fromClause, 0, $spacePos); |
---|
298 | $alias = substr($fromClause, $spacePos + 1); |
---|
299 | |
---|
300 | $fromClause = new Query\Expr\From($from, $alias); |
---|
301 | } |
---|
302 | |
---|
303 | $entities[] = $fromClause->getFrom(); |
---|
304 | } |
---|
305 | |
---|
306 | return $entities; |
---|
307 | } |
---|
308 | |
---|
309 | /** |
---|
310 | * Sets a query parameter for the query being constructed. |
---|
311 | * |
---|
312 | * <code> |
---|
313 | * $qb = $em->createQueryBuilder() |
---|
314 | * ->select('u') |
---|
315 | * ->from('User', 'u') |
---|
316 | * ->where('u.id = :user_id') |
---|
317 | * ->setParameter('user_id', 1); |
---|
318 | * </code> |
---|
319 | * |
---|
320 | * @param string|integer $key The parameter position or name. |
---|
321 | * @param mixed $value The parameter value. |
---|
322 | * @param string|null $type PDO::PARAM_* or \Doctrine\DBAL\Types\Type::* constant |
---|
323 | * @return QueryBuilder This QueryBuilder instance. |
---|
324 | */ |
---|
325 | public function setParameter($key, $value, $type = null) |
---|
326 | { |
---|
327 | $key = trim($key, ':'); |
---|
328 | |
---|
329 | if ($type === null) { |
---|
330 | $type = Query\ParameterTypeInferer::inferType($value); |
---|
331 | } |
---|
332 | |
---|
333 | $this->_paramTypes[$key] = $type; |
---|
334 | $this->_params[$key] = $value; |
---|
335 | |
---|
336 | return $this; |
---|
337 | } |
---|
338 | |
---|
339 | /** |
---|
340 | * Sets a collection of query parameters for the query being constructed. |
---|
341 | * |
---|
342 | * <code> |
---|
343 | * $qb = $em->createQueryBuilder() |
---|
344 | * ->select('u') |
---|
345 | * ->from('User', 'u') |
---|
346 | * ->where('u.id = :user_id1 OR u.id = :user_id2') |
---|
347 | * ->setParameters(array( |
---|
348 | * 'user_id1' => 1, |
---|
349 | * 'user_id2' => 2 |
---|
350 | )); |
---|
351 | * </code> |
---|
352 | * |
---|
353 | * @param array $params The query parameters to set. |
---|
354 | * @return QueryBuilder This QueryBuilder instance. |
---|
355 | */ |
---|
356 | public function setParameters(array $params, array $types = array()) |
---|
357 | { |
---|
358 | foreach ($params as $key => $value) { |
---|
359 | $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null); |
---|
360 | } |
---|
361 | |
---|
362 | return $this; |
---|
363 | } |
---|
364 | |
---|
365 | /** |
---|
366 | * Gets all defined query parameters for the query being constructed. |
---|
367 | * |
---|
368 | * @return array The currently defined query parameters. |
---|
369 | */ |
---|
370 | public function getParameters() |
---|
371 | { |
---|
372 | return $this->_params; |
---|
373 | } |
---|
374 | |
---|
375 | /** |
---|
376 | * Gets a (previously set) query parameter of the query being constructed. |
---|
377 | * |
---|
378 | * @param mixed $key The key (index or name) of the bound parameter. |
---|
379 | * @return mixed The value of the bound parameter. |
---|
380 | */ |
---|
381 | public function getParameter($key) |
---|
382 | { |
---|
383 | return isset($this->_params[$key]) ? $this->_params[$key] : null; |
---|
384 | } |
---|
385 | |
---|
386 | /** |
---|
387 | * Sets the position of the first result to retrieve (the "offset"). |
---|
388 | * |
---|
389 | * @param integer $firstResult The first result to return. |
---|
390 | * @return QueryBuilder This QueryBuilder instance. |
---|
391 | */ |
---|
392 | public function setFirstResult($firstResult) |
---|
393 | { |
---|
394 | $this->_firstResult = $firstResult; |
---|
395 | |
---|
396 | return $this; |
---|
397 | } |
---|
398 | |
---|
399 | /** |
---|
400 | * Gets the position of the first result the query object was set to retrieve (the "offset"). |
---|
401 | * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder. |
---|
402 | * |
---|
403 | * @return integer The position of the first result. |
---|
404 | */ |
---|
405 | public function getFirstResult() |
---|
406 | { |
---|
407 | return $this->_firstResult; |
---|
408 | } |
---|
409 | |
---|
410 | /** |
---|
411 | * Sets the maximum number of results to retrieve (the "limit"). |
---|
412 | * |
---|
413 | * @param integer $maxResults The maximum number of results to retrieve. |
---|
414 | * @return QueryBuilder This QueryBuilder instance. |
---|
415 | */ |
---|
416 | public function setMaxResults($maxResults) |
---|
417 | { |
---|
418 | $this->_maxResults = $maxResults; |
---|
419 | |
---|
420 | return $this; |
---|
421 | } |
---|
422 | |
---|
423 | /** |
---|
424 | * Gets the maximum number of results the query object was set to retrieve (the "limit"). |
---|
425 | * Returns NULL if {@link setMaxResults} was not applied to this query builder. |
---|
426 | * |
---|
427 | * @return integer Maximum number of results. |
---|
428 | */ |
---|
429 | public function getMaxResults() |
---|
430 | { |
---|
431 | return $this->_maxResults; |
---|
432 | } |
---|
433 | |
---|
434 | /** |
---|
435 | * Either appends to or replaces a single, generic query part. |
---|
436 | * |
---|
437 | * The available parts are: 'select', 'from', 'join', 'set', 'where', |
---|
438 | * 'groupBy', 'having' and 'orderBy'. |
---|
439 | * |
---|
440 | * @param string $dqlPartName |
---|
441 | * @param string $dqlPart |
---|
442 | * @param string $append |
---|
443 | * @return QueryBuilder This QueryBuilder instance. |
---|
444 | */ |
---|
445 | public function add($dqlPartName, $dqlPart, $append = false) |
---|
446 | { |
---|
447 | $isMultiple = is_array($this->_dqlParts[$dqlPartName]); |
---|
448 | |
---|
449 | // This is introduced for backwards compatibility reasons. |
---|
450 | // TODO: Remove for 3.0 |
---|
451 | if ($dqlPartName == 'join') { |
---|
452 | $newDqlPart = array(); |
---|
453 | foreach ($dqlPart AS $k => $v) { |
---|
454 | if (is_numeric($k)) { |
---|
455 | $newDqlPart[$this->getRootAlias()] = $v; |
---|
456 | } else { |
---|
457 | $newDqlPart[$k] = $v; |
---|
458 | } |
---|
459 | } |
---|
460 | $dqlPart = $newDqlPart; |
---|
461 | } |
---|
462 | |
---|
463 | if ($append && $isMultiple) { |
---|
464 | if (is_array($dqlPart)) { |
---|
465 | $key = key($dqlPart); |
---|
466 | |
---|
467 | $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key]; |
---|
468 | } else { |
---|
469 | $this->_dqlParts[$dqlPartName][] = $dqlPart; |
---|
470 | } |
---|
471 | } else { |
---|
472 | $this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart; |
---|
473 | } |
---|
474 | |
---|
475 | $this->_state = self::STATE_DIRTY; |
---|
476 | |
---|
477 | return $this; |
---|
478 | } |
---|
479 | |
---|
480 | /** |
---|
481 | * Specifies an item that is to be returned in the query result. |
---|
482 | * Replaces any previously specified selections, if any. |
---|
483 | * |
---|
484 | * <code> |
---|
485 | * $qb = $em->createQueryBuilder() |
---|
486 | * ->select('u', 'p') |
---|
487 | * ->from('User', 'u') |
---|
488 | * ->leftJoin('u.Phonenumbers', 'p'); |
---|
489 | * </code> |
---|
490 | * |
---|
491 | * @param mixed $select The selection expressions. |
---|
492 | * @return QueryBuilder This QueryBuilder instance. |
---|
493 | */ |
---|
494 | public function select($select = null) |
---|
495 | { |
---|
496 | $this->_type = self::SELECT; |
---|
497 | |
---|
498 | if (empty($select)) { |
---|
499 | return $this; |
---|
500 | } |
---|
501 | |
---|
502 | $selects = is_array($select) ? $select : func_get_args(); |
---|
503 | |
---|
504 | return $this->add('select', new Expr\Select($selects), false); |
---|
505 | } |
---|
506 | |
---|
507 | /** |
---|
508 | * Add a DISTINCT flag to this query. |
---|
509 | * |
---|
510 | * <code> |
---|
511 | * $qb = $em->createQueryBuilder() |
---|
512 | * ->select('u') |
---|
513 | * ->distinct() |
---|
514 | * ->from('User', 'u'); |
---|
515 | * </code> |
---|
516 | * |
---|
517 | * @param bool |
---|
518 | * @return QueryBuilder |
---|
519 | */ |
---|
520 | public function distinct($flag = true) |
---|
521 | { |
---|
522 | $this->_dqlParts['distinct'] = (bool) $flag; |
---|
523 | return $this; |
---|
524 | } |
---|
525 | |
---|
526 | /** |
---|
527 | * Adds an item that is to be returned in the query result. |
---|
528 | * |
---|
529 | * <code> |
---|
530 | * $qb = $em->createQueryBuilder() |
---|
531 | * ->select('u') |
---|
532 | * ->addSelect('p') |
---|
533 | * ->from('User', 'u') |
---|
534 | * ->leftJoin('u.Phonenumbers', 'p'); |
---|
535 | * </code> |
---|
536 | * |
---|
537 | * @param mixed $select The selection expression. |
---|
538 | * @return QueryBuilder This QueryBuilder instance. |
---|
539 | */ |
---|
540 | public function addSelect($select = null) |
---|
541 | { |
---|
542 | $this->_type = self::SELECT; |
---|
543 | |
---|
544 | if (empty($select)) { |
---|
545 | return $this; |
---|
546 | } |
---|
547 | |
---|
548 | $selects = is_array($select) ? $select : func_get_args(); |
---|
549 | |
---|
550 | return $this->add('select', new Expr\Select($selects), true); |
---|
551 | } |
---|
552 | |
---|
553 | /** |
---|
554 | * Turns the query being built into a bulk delete query that ranges over |
---|
555 | * a certain entity type. |
---|
556 | * |
---|
557 | * <code> |
---|
558 | * $qb = $em->createQueryBuilder() |
---|
559 | * ->delete('User', 'u') |
---|
560 | * ->where('u.id = :user_id'); |
---|
561 | * ->setParameter('user_id', 1); |
---|
562 | * </code> |
---|
563 | * |
---|
564 | * @param string $delete The class/type whose instances are subject to the deletion. |
---|
565 | * @param string $alias The class/type alias used in the constructed query. |
---|
566 | * @return QueryBuilder This QueryBuilder instance. |
---|
567 | */ |
---|
568 | public function delete($delete = null, $alias = null) |
---|
569 | { |
---|
570 | $this->_type = self::DELETE; |
---|
571 | |
---|
572 | if ( ! $delete) { |
---|
573 | return $this; |
---|
574 | } |
---|
575 | |
---|
576 | return $this->add('from', new Expr\From($delete, $alias)); |
---|
577 | } |
---|
578 | |
---|
579 | /** |
---|
580 | * Turns the query being built into a bulk update query that ranges over |
---|
581 | * a certain entity type. |
---|
582 | * |
---|
583 | * <code> |
---|
584 | * $qb = $em->createQueryBuilder() |
---|
585 | * ->update('User', 'u') |
---|
586 | * ->set('u.password', md5('password')) |
---|
587 | * ->where('u.id = ?'); |
---|
588 | * </code> |
---|
589 | * |
---|
590 | * @param string $update The class/type whose instances are subject to the update. |
---|
591 | * @param string $alias The class/type alias used in the constructed query. |
---|
592 | * @return QueryBuilder This QueryBuilder instance. |
---|
593 | */ |
---|
594 | public function update($update = null, $alias = null) |
---|
595 | { |
---|
596 | $this->_type = self::UPDATE; |
---|
597 | |
---|
598 | if ( ! $update) { |
---|
599 | return $this; |
---|
600 | } |
---|
601 | |
---|
602 | return $this->add('from', new Expr\From($update, $alias)); |
---|
603 | } |
---|
604 | |
---|
605 | /** |
---|
606 | * Create and add a query root corresponding to the entity identified by the given alias, |
---|
607 | * forming a cartesian product with any existing query roots. |
---|
608 | * |
---|
609 | * <code> |
---|
610 | * $qb = $em->createQueryBuilder() |
---|
611 | * ->select('u') |
---|
612 | * ->from('User', 'u') |
---|
613 | * </code> |
---|
614 | * |
---|
615 | * @param string $from The class name. |
---|
616 | * @param string $alias The alias of the class. |
---|
617 | * @param string $indexBy The index for the from. |
---|
618 | * @return QueryBuilder This QueryBuilder instance. |
---|
619 | */ |
---|
620 | public function from($from, $alias, $indexBy = null) |
---|
621 | { |
---|
622 | return $this->add('from', new Expr\From($from, $alias, $indexBy), true); |
---|
623 | } |
---|
624 | |
---|
625 | /** |
---|
626 | * Creates and adds a join over an entity association to the query. |
---|
627 | * |
---|
628 | * The entities in the joined association will be fetched as part of the query |
---|
629 | * result if the alias used for the joined association is placed in the select |
---|
630 | * expressions. |
---|
631 | * |
---|
632 | * <code> |
---|
633 | * $qb = $em->createQueryBuilder() |
---|
634 | * ->select('u') |
---|
635 | * ->from('User', 'u') |
---|
636 | * ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); |
---|
637 | * </code> |
---|
638 | * |
---|
639 | * @param string $join The relationship to join |
---|
640 | * @param string $alias The alias of the join |
---|
641 | * @param string $conditionType The condition type constant. Either ON or WITH. |
---|
642 | * @param string $condition The condition for the join |
---|
643 | * @param string $indexBy The index for the join |
---|
644 | * @return QueryBuilder This QueryBuilder instance. |
---|
645 | */ |
---|
646 | public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null) |
---|
647 | { |
---|
648 | return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy); |
---|
649 | } |
---|
650 | |
---|
651 | /** |
---|
652 | * Creates and adds a join over an entity association to the query. |
---|
653 | * |
---|
654 | * The entities in the joined association will be fetched as part of the query |
---|
655 | * result if the alias used for the joined association is placed in the select |
---|
656 | * expressions. |
---|
657 | * |
---|
658 | * [php] |
---|
659 | * $qb = $em->createQueryBuilder() |
---|
660 | * ->select('u') |
---|
661 | * ->from('User', 'u') |
---|
662 | * ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); |
---|
663 | * |
---|
664 | * @param string $join The relationship to join |
---|
665 | * @param string $alias The alias of the join |
---|
666 | * @param string $conditionType The condition type constant. Either ON or WITH. |
---|
667 | * @param string $condition The condition for the join |
---|
668 | * @param string $indexBy The index for the join |
---|
669 | * @return QueryBuilder This QueryBuilder instance. |
---|
670 | */ |
---|
671 | public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) |
---|
672 | { |
---|
673 | $rootAlias = substr($join, 0, strpos($join, '.')); |
---|
674 | |
---|
675 | if ( ! in_array($rootAlias, $this->getRootAliases())) { |
---|
676 | $rootAlias = $this->getRootAlias(); |
---|
677 | } |
---|
678 | |
---|
679 | $join = new Expr\Join( |
---|
680 | Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy |
---|
681 | ); |
---|
682 | |
---|
683 | return $this->add('join', array($rootAlias => $join), true); |
---|
684 | } |
---|
685 | |
---|
686 | /** |
---|
687 | * Creates and adds a left join over an entity association to the query. |
---|
688 | * |
---|
689 | * The entities in the joined association will be fetched as part of the query |
---|
690 | * result if the alias used for the joined association is placed in the select |
---|
691 | * expressions. |
---|
692 | * |
---|
693 | * <code> |
---|
694 | * $qb = $em->createQueryBuilder() |
---|
695 | * ->select('u') |
---|
696 | * ->from('User', 'u') |
---|
697 | * ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1'); |
---|
698 | * </code> |
---|
699 | * |
---|
700 | * @param string $join The relationship to join |
---|
701 | * @param string $alias The alias of the join |
---|
702 | * @param string $conditionType The condition type constant. Either ON or WITH. |
---|
703 | * @param string $condition The condition for the join |
---|
704 | * @param string $indexBy The index for the join |
---|
705 | * @return QueryBuilder This QueryBuilder instance. |
---|
706 | */ |
---|
707 | public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) |
---|
708 | { |
---|
709 | $rootAlias = substr($join, 0, strpos($join, '.')); |
---|
710 | |
---|
711 | if ( ! in_array($rootAlias, $this->getRootAliases())) { |
---|
712 | $rootAlias = $this->getRootAlias(); |
---|
713 | } |
---|
714 | |
---|
715 | $join = new Expr\Join( |
---|
716 | Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy |
---|
717 | ); |
---|
718 | |
---|
719 | return $this->add('join', array($rootAlias => $join), true); |
---|
720 | } |
---|
721 | |
---|
722 | /** |
---|
723 | * Sets a new value for a field in a bulk update query. |
---|
724 | * |
---|
725 | * <code> |
---|
726 | * $qb = $em->createQueryBuilder() |
---|
727 | * ->update('User', 'u') |
---|
728 | * ->set('u.password', md5('password')) |
---|
729 | * ->where('u.id = ?'); |
---|
730 | * </code> |
---|
731 | * |
---|
732 | * @param string $key The key/field to set. |
---|
733 | * @param string $value The value, expression, placeholder, etc. |
---|
734 | * @return QueryBuilder This QueryBuilder instance. |
---|
735 | */ |
---|
736 | public function set($key, $value) |
---|
737 | { |
---|
738 | return $this->add('set', new Expr\Comparison($key, Expr\Comparison::EQ, $value), true); |
---|
739 | } |
---|
740 | |
---|
741 | /** |
---|
742 | * Specifies one or more restrictions to the query result. |
---|
743 | * Replaces any previously specified restrictions, if any. |
---|
744 | * |
---|
745 | * <code> |
---|
746 | * $qb = $em->createQueryBuilder() |
---|
747 | * ->select('u') |
---|
748 | * ->from('User', 'u') |
---|
749 | * ->where('u.id = ?'); |
---|
750 | * |
---|
751 | * // You can optionally programatically build and/or expressions |
---|
752 | * $qb = $em->createQueryBuilder(); |
---|
753 | * |
---|
754 | * $or = $qb->expr()->orx(); |
---|
755 | * $or->add($qb->expr()->eq('u.id', 1)); |
---|
756 | * $or->add($qb->expr()->eq('u.id', 2)); |
---|
757 | * |
---|
758 | * $qb->update('User', 'u') |
---|
759 | * ->set('u.password', md5('password')) |
---|
760 | * ->where($or); |
---|
761 | * </code> |
---|
762 | * |
---|
763 | * @param mixed $predicates The restriction predicates. |
---|
764 | * @return QueryBuilder This QueryBuilder instance. |
---|
765 | */ |
---|
766 | public function where($predicates) |
---|
767 | { |
---|
768 | if ( ! (func_num_args() == 1 && $predicates instanceof Expr\Composite)) { |
---|
769 | $predicates = new Expr\Andx(func_get_args()); |
---|
770 | } |
---|
771 | |
---|
772 | return $this->add('where', $predicates); |
---|
773 | } |
---|
774 | |
---|
775 | /** |
---|
776 | * Adds one or more restrictions to the query results, forming a logical |
---|
777 | * conjunction with any previously specified restrictions. |
---|
778 | * |
---|
779 | * <code> |
---|
780 | * $qb = $em->createQueryBuilder() |
---|
781 | * ->select('u') |
---|
782 | * ->from('User', 'u') |
---|
783 | * ->where('u.username LIKE ?') |
---|
784 | * ->andWhere('u.is_active = 1'); |
---|
785 | * </code> |
---|
786 | * |
---|
787 | * @param mixed $where The query restrictions. |
---|
788 | * @return QueryBuilder This QueryBuilder instance. |
---|
789 | * @see where() |
---|
790 | */ |
---|
791 | public function andWhere($where) |
---|
792 | { |
---|
793 | $where = $this->getDQLPart('where'); |
---|
794 | $args = func_get_args(); |
---|
795 | |
---|
796 | if ($where instanceof Expr\Andx) { |
---|
797 | $where->addMultiple($args); |
---|
798 | } else { |
---|
799 | array_unshift($args, $where); |
---|
800 | $where = new Expr\Andx($args); |
---|
801 | } |
---|
802 | |
---|
803 | return $this->add('where', $where, true); |
---|
804 | } |
---|
805 | |
---|
806 | /** |
---|
807 | * Adds one or more restrictions to the query results, forming a logical |
---|
808 | * disjunction with any previously specified restrictions. |
---|
809 | * |
---|
810 | * <code> |
---|
811 | * $qb = $em->createQueryBuilder() |
---|
812 | * ->select('u') |
---|
813 | * ->from('User', 'u') |
---|
814 | * ->where('u.id = 1') |
---|
815 | * ->orWhere('u.id = 2'); |
---|
816 | * </code> |
---|
817 | * |
---|
818 | * @param mixed $where The WHERE statement |
---|
819 | * @return QueryBuilder $qb |
---|
820 | * @see where() |
---|
821 | */ |
---|
822 | public function orWhere($where) |
---|
823 | { |
---|
824 | $where = $this->getDqlPart('where'); |
---|
825 | $args = func_get_args(); |
---|
826 | |
---|
827 | if ($where instanceof Expr\Orx) { |
---|
828 | $where->addMultiple($args); |
---|
829 | } else { |
---|
830 | array_unshift($args, $where); |
---|
831 | $where = new Expr\Orx($args); |
---|
832 | } |
---|
833 | |
---|
834 | return $this->add('where', $where, true); |
---|
835 | } |
---|
836 | |
---|
837 | /** |
---|
838 | * Specifies a grouping over the results of the query. |
---|
839 | * Replaces any previously specified groupings, if any. |
---|
840 | * |
---|
841 | * <code> |
---|
842 | * $qb = $em->createQueryBuilder() |
---|
843 | * ->select('u') |
---|
844 | * ->from('User', 'u') |
---|
845 | * ->groupBy('u.id'); |
---|
846 | * </code> |
---|
847 | * |
---|
848 | * @param string $groupBy The grouping expression. |
---|
849 | * @return QueryBuilder This QueryBuilder instance. |
---|
850 | */ |
---|
851 | public function groupBy($groupBy) |
---|
852 | { |
---|
853 | return $this->add('groupBy', new Expr\GroupBy(func_get_args())); |
---|
854 | } |
---|
855 | |
---|
856 | |
---|
857 | /** |
---|
858 | * Adds a grouping expression to the query. |
---|
859 | * |
---|
860 | * <code> |
---|
861 | * $qb = $em->createQueryBuilder() |
---|
862 | * ->select('u') |
---|
863 | * ->from('User', 'u') |
---|
864 | * ->groupBy('u.lastLogin'); |
---|
865 | * ->addGroupBy('u.createdAt') |
---|
866 | * </code> |
---|
867 | * |
---|
868 | * @param string $groupBy The grouping expression. |
---|
869 | * @return QueryBuilder This QueryBuilder instance. |
---|
870 | */ |
---|
871 | public function addGroupBy($groupBy) |
---|
872 | { |
---|
873 | return $this->add('groupBy', new Expr\GroupBy(func_get_args()), true); |
---|
874 | } |
---|
875 | |
---|
876 | /** |
---|
877 | * Specifies a restriction over the groups of the query. |
---|
878 | * Replaces any previous having restrictions, if any. |
---|
879 | * |
---|
880 | * @param mixed $having The restriction over the groups. |
---|
881 | * @return QueryBuilder This QueryBuilder instance. |
---|
882 | */ |
---|
883 | public function having($having) |
---|
884 | { |
---|
885 | if ( ! (func_num_args() == 1 && ($having instanceof Expr\Andx || $having instanceof Expr\Orx))) { |
---|
886 | $having = new Expr\Andx(func_get_args()); |
---|
887 | } |
---|
888 | |
---|
889 | return $this->add('having', $having); |
---|
890 | } |
---|
891 | |
---|
892 | /** |
---|
893 | * Adds a restriction over the groups of the query, forming a logical |
---|
894 | * conjunction with any existing having restrictions. |
---|
895 | * |
---|
896 | * @param mixed $having The restriction to append. |
---|
897 | * @return QueryBuilder This QueryBuilder instance. |
---|
898 | */ |
---|
899 | public function andHaving($having) |
---|
900 | { |
---|
901 | $having = $this->getDqlPart('having'); |
---|
902 | $args = func_get_args(); |
---|
903 | |
---|
904 | if ($having instanceof Expr\Andx) { |
---|
905 | $having->addMultiple($args); |
---|
906 | } else { |
---|
907 | array_unshift($args, $having); |
---|
908 | $having = new Expr\Andx($args); |
---|
909 | } |
---|
910 | |
---|
911 | return $this->add('having', $having); |
---|
912 | } |
---|
913 | |
---|
914 | /** |
---|
915 | * Adds a restriction over the groups of the query, forming a logical |
---|
916 | * disjunction with any existing having restrictions. |
---|
917 | * |
---|
918 | * @param mixed $having The restriction to add. |
---|
919 | * @return QueryBuilder This QueryBuilder instance. |
---|
920 | */ |
---|
921 | public function orHaving($having) |
---|
922 | { |
---|
923 | $having = $this->getDqlPart('having'); |
---|
924 | $args = func_get_args(); |
---|
925 | |
---|
926 | if ($having instanceof Expr\Orx) { |
---|
927 | $having->addMultiple($args); |
---|
928 | } else { |
---|
929 | array_unshift($args, $having); |
---|
930 | $having = new Expr\Orx($args); |
---|
931 | } |
---|
932 | |
---|
933 | return $this->add('having', $having); |
---|
934 | } |
---|
935 | |
---|
936 | /** |
---|
937 | * Specifies an ordering for the query results. |
---|
938 | * Replaces any previously specified orderings, if any. |
---|
939 | * |
---|
940 | * @param string $sort The ordering expression. |
---|
941 | * @param string $order The ordering direction. |
---|
942 | * @return QueryBuilder This QueryBuilder instance. |
---|
943 | */ |
---|
944 | public function orderBy($sort, $order = null) |
---|
945 | { |
---|
946 | return $this->add('orderBy', $sort instanceof Expr\OrderBy ? $sort |
---|
947 | : new Expr\OrderBy($sort, $order)); |
---|
948 | } |
---|
949 | |
---|
950 | /** |
---|
951 | * Adds an ordering to the query results. |
---|
952 | * |
---|
953 | * @param string $sort The ordering expression. |
---|
954 | * @param string $order The ordering direction. |
---|
955 | * @return QueryBuilder This QueryBuilder instance. |
---|
956 | */ |
---|
957 | public function addOrderBy($sort, $order = null) |
---|
958 | { |
---|
959 | return $this->add('orderBy', new Expr\OrderBy($sort, $order), true); |
---|
960 | } |
---|
961 | |
---|
962 | /** |
---|
963 | * Get a query part by its name. |
---|
964 | * |
---|
965 | * @param string $queryPartName |
---|
966 | * @return mixed $queryPart |
---|
967 | * @todo Rename: getQueryPart (or remove?) |
---|
968 | */ |
---|
969 | public function getDQLPart($queryPartName) |
---|
970 | { |
---|
971 | return $this->_dqlParts[$queryPartName]; |
---|
972 | } |
---|
973 | |
---|
974 | /** |
---|
975 | * Get all query parts. |
---|
976 | * |
---|
977 | * @return array $dqlParts |
---|
978 | * @todo Rename: getQueryParts (or remove?) |
---|
979 | */ |
---|
980 | public function getDQLParts() |
---|
981 | { |
---|
982 | return $this->_dqlParts; |
---|
983 | } |
---|
984 | |
---|
985 | private function _getDQLForDelete() |
---|
986 | { |
---|
987 | return 'DELETE' |
---|
988 | . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) |
---|
989 | . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) |
---|
990 | . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); |
---|
991 | } |
---|
992 | |
---|
993 | private function _getDQLForUpdate() |
---|
994 | { |
---|
995 | return 'UPDATE' |
---|
996 | . $this->_getReducedDQLQueryPart('from', array('pre' => ' ', 'separator' => ', ')) |
---|
997 | . $this->_getReducedDQLQueryPart('set', array('pre' => ' SET ', 'separator' => ', ')) |
---|
998 | . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) |
---|
999 | . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); |
---|
1000 | } |
---|
1001 | |
---|
1002 | private function _getDQLForSelect() |
---|
1003 | { |
---|
1004 | $dql = 'SELECT' |
---|
1005 | . ($this->_dqlParts['distinct']===true ? ' DISTINCT' : '') |
---|
1006 | . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', ')); |
---|
1007 | |
---|
1008 | $fromParts = $this->getDQLPart('from'); |
---|
1009 | $joinParts = $this->getDQLPart('join'); |
---|
1010 | $fromClauses = array(); |
---|
1011 | |
---|
1012 | // Loop through all FROM clauses |
---|
1013 | if ( ! empty($fromParts)) { |
---|
1014 | $dql .= ' FROM '; |
---|
1015 | |
---|
1016 | foreach ($fromParts as $from) { |
---|
1017 | $fromClause = (string) $from; |
---|
1018 | |
---|
1019 | if ($from instanceof Expr\From && isset($joinParts[$from->getAlias()])) { |
---|
1020 | foreach ($joinParts[$from->getAlias()] as $join) { |
---|
1021 | $fromClause .= ' ' . ((string) $join); |
---|
1022 | } |
---|
1023 | } |
---|
1024 | |
---|
1025 | $fromClauses[] = $fromClause; |
---|
1026 | } |
---|
1027 | } |
---|
1028 | |
---|
1029 | $dql .= implode(', ', $fromClauses) |
---|
1030 | . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) |
---|
1031 | . $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', ')) |
---|
1032 | . $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING ')) |
---|
1033 | . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); |
---|
1034 | |
---|
1035 | return $dql; |
---|
1036 | } |
---|
1037 | |
---|
1038 | private function _getReducedDQLQueryPart($queryPartName, $options = array()) |
---|
1039 | { |
---|
1040 | $queryPart = $this->getDQLPart($queryPartName); |
---|
1041 | |
---|
1042 | if (empty($queryPart)) { |
---|
1043 | return (isset($options['empty']) ? $options['empty'] : ''); |
---|
1044 | } |
---|
1045 | |
---|
1046 | return (isset($options['pre']) ? $options['pre'] : '') |
---|
1047 | . (is_array($queryPart) ? implode($options['separator'], $queryPart) : $queryPart) |
---|
1048 | . (isset($options['post']) ? $options['post'] : ''); |
---|
1049 | } |
---|
1050 | |
---|
1051 | /** |
---|
1052 | * Reset DQL parts |
---|
1053 | * |
---|
1054 | * @param array $parts |
---|
1055 | * @return QueryBuilder |
---|
1056 | */ |
---|
1057 | public function resetDQLParts($parts = null) |
---|
1058 | { |
---|
1059 | if (is_null($parts)) { |
---|
1060 | $parts = array_keys($this->_dqlParts); |
---|
1061 | } |
---|
1062 | foreach ($parts as $part) { |
---|
1063 | $this->resetDQLPart($part); |
---|
1064 | } |
---|
1065 | return $this; |
---|
1066 | } |
---|
1067 | |
---|
1068 | /** |
---|
1069 | * Reset single DQL part |
---|
1070 | * |
---|
1071 | * @param string $part |
---|
1072 | * @return QueryBuilder; |
---|
1073 | */ |
---|
1074 | public function resetDQLPart($part) |
---|
1075 | { |
---|
1076 | if (is_array($this->_dqlParts[$part])) { |
---|
1077 | $this->_dqlParts[$part] = array(); |
---|
1078 | } else { |
---|
1079 | $this->_dqlParts[$part] = null; |
---|
1080 | } |
---|
1081 | $this->_state = self::STATE_DIRTY; |
---|
1082 | return $this; |
---|
1083 | } |
---|
1084 | |
---|
1085 | /** |
---|
1086 | * Gets a string representation of this QueryBuilder which corresponds to |
---|
1087 | * the final DQL query being constructed. |
---|
1088 | * |
---|
1089 | * @return string The string representation of this QueryBuilder. |
---|
1090 | */ |
---|
1091 | public function __toString() |
---|
1092 | { |
---|
1093 | return $this->getDQL(); |
---|
1094 | } |
---|
1095 | |
---|
1096 | /** |
---|
1097 | * Deep clone of all expression objects in the DQL parts. |
---|
1098 | * |
---|
1099 | * @return void |
---|
1100 | */ |
---|
1101 | public function __clone() |
---|
1102 | { |
---|
1103 | foreach ($this->_dqlParts AS $part => $elements) { |
---|
1104 | if (is_array($this->_dqlParts[$part])) { |
---|
1105 | foreach ($this->_dqlParts[$part] AS $idx => $element) { |
---|
1106 | if (is_object($element)) { |
---|
1107 | $this->_dqlParts[$part][$idx] = clone $element; |
---|
1108 | } |
---|
1109 | } |
---|
1110 | } else if (\is_object($elements)) { |
---|
1111 | $this->_dqlParts[$part] = clone $elements; |
---|
1112 | } |
---|
1113 | } |
---|
1114 | } |
---|
1115 | } |
---|