QueryBuilder.php 33KB

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