QueryBuilder.php 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  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. $key = trim($key, ':');
  290. if ($type === null) {
  291. $type = Query\ParameterTypeInferer::inferType($value);
  292. }
  293. $this->_paramTypes[$key] = $type;
  294. $this->_params[$key] = $value;
  295. return $this;
  296. }
  297. /**
  298. * Sets a collection of query parameters for the query being constructed.
  299. *
  300. * <code>
  301. * $qb = $em->createQueryBuilder()
  302. * ->select('u')
  303. * ->from('User', 'u')
  304. * ->where('u.id = :user_id1 OR u.id = :user_id2')
  305. * ->setParameters(array(
  306. * 'user_id1' => 1,
  307. * 'user_id2' => 2
  308. ));
  309. * </code>
  310. *
  311. * @param array $params The query parameters to set.
  312. * @return QueryBuilder This QueryBuilder instance.
  313. */
  314. public function setParameters(array $params, array $types = array())
  315. {
  316. foreach ($params as $key => $value) {
  317. $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
  318. }
  319. return $this;
  320. }
  321. /**
  322. * Gets all defined query parameters for the query being constructed.
  323. *
  324. * @return array The currently defined query parameters.
  325. */
  326. public function getParameters()
  327. {
  328. return $this->_params;
  329. }
  330. /**
  331. * Gets a (previously set) query parameter of the query being constructed.
  332. *
  333. * @param mixed $key The key (index or name) of the bound parameter.
  334. * @return mixed The value of the bound parameter.
  335. */
  336. public function getParameter($key)
  337. {
  338. return isset($this->_params[$key]) ? $this->_params[$key] : null;
  339. }
  340. /**
  341. * Sets the position of the first result to retrieve (the "offset").
  342. *
  343. * @param integer $firstResult The first result to return.
  344. * @return QueryBuilder This QueryBuilder instance.
  345. */
  346. public function setFirstResult($firstResult)
  347. {
  348. $this->_firstResult = $firstResult;
  349. return $this;
  350. }
  351. /**
  352. * Gets the position of the first result the query object was set to retrieve (the "offset").
  353. * Returns NULL if {@link setFirstResult} was not applied to this QueryBuilder.
  354. *
  355. * @return integer The position of the first result.
  356. */
  357. public function getFirstResult()
  358. {
  359. return $this->_firstResult;
  360. }
  361. /**
  362. * Sets the maximum number of results to retrieve (the "limit").
  363. *
  364. * @param integer $maxResults The maximum number of results to retrieve.
  365. * @return QueryBuilder This QueryBuilder instance.
  366. */
  367. public function setMaxResults($maxResults)
  368. {
  369. $this->_maxResults = $maxResults;
  370. return $this;
  371. }
  372. /**
  373. * Gets the maximum number of results the query object was set to retrieve (the "limit").
  374. * Returns NULL if {@link setMaxResults} was not applied to this query builder.
  375. *
  376. * @return integer Maximum number of results.
  377. */
  378. public function getMaxResults()
  379. {
  380. return $this->_maxResults;
  381. }
  382. /**
  383. * Either appends to or replaces a single, generic query part.
  384. *
  385. * The available parts are: 'select', 'from', 'join', 'set', 'where',
  386. * 'groupBy', 'having' and 'orderBy'.
  387. *
  388. * @param string $dqlPartName
  389. * @param string $dqlPart
  390. * @param string $append
  391. * @return QueryBuilder This QueryBuilder instance.
  392. */
  393. public function add($dqlPartName, $dqlPart, $append = false)
  394. {
  395. $isMultiple = is_array($this->_dqlParts[$dqlPartName]);
  396. // This is introduced for backwards compatibility reasons.
  397. // TODO: Remove for 3.0
  398. if ($dqlPartName == 'join') {
  399. $newDqlPart = array();
  400. foreach ($dqlPart AS $k => $v) {
  401. if (is_numeric($k)) {
  402. $newDqlPart[$this->getRootAlias()] = $v;
  403. } else {
  404. $newDqlPart[$k] = $v;
  405. }
  406. }
  407. $dqlPart = $newDqlPart;
  408. }
  409. if ($append && $isMultiple) {
  410. if (is_array($dqlPart)) {
  411. $key = key($dqlPart);
  412. $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
  413. } else {
  414. $this->_dqlParts[$dqlPartName][] = $dqlPart;
  415. }
  416. } else {
  417. $this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
  418. }
  419. $this->_state = self::STATE_DIRTY;
  420. return $this;
  421. }
  422. /**
  423. * Specifies an item that is to be returned in the query result.
  424. * Replaces any previously specified selections, if any.
  425. *
  426. * <code>
  427. * $qb = $em->createQueryBuilder()
  428. * ->select('u', 'p')
  429. * ->from('User', 'u')
  430. * ->leftJoin('u.Phonenumbers', 'p');
  431. * </code>
  432. *
  433. * @param mixed $select The selection expressions.
  434. * @return QueryBuilder This QueryBuilder instance.
  435. */
  436. public function select($select = null)
  437. {
  438. $this->_type = self::SELECT;
  439. if (empty($select)) {
  440. return $this;
  441. }
  442. $selects = is_array($select) ? $select : func_get_args();
  443. return $this->add('select', new Expr\Select($selects), false);
  444. }
  445. /**
  446. * Add a DISTINCT flag to this query.
  447. *
  448. * <code>
  449. * $qb = $em->createQueryBuilder()
  450. * ->select('u')
  451. * ->distinct()
  452. * ->from('User', 'u');
  453. * </code>
  454. *
  455. * @param bool
  456. * @return QueryBuilder
  457. */
  458. public function distinct($flag = true)
  459. {
  460. $this->_dqlParts['distinct'] = (bool) $flag;
  461. return $this;
  462. }
  463. /**
  464. * Adds an item that is to be returned in the query result.
  465. *
  466. * <code>
  467. * $qb = $em->createQueryBuilder()
  468. * ->select('u')
  469. * ->addSelect('p')
  470. * ->from('User', 'u')
  471. * ->leftJoin('u.Phonenumbers', 'p');
  472. * </code>
  473. *
  474. * @param mixed $select The selection expression.
  475. * @return QueryBuilder This QueryBuilder instance.
  476. */
  477. public function addSelect($select = null)
  478. {
  479. $this->_type = self::SELECT;
  480. if (empty($select)) {
  481. return $this;
  482. }
  483. $selects = is_array($select) ? $select : func_get_args();
  484. return $this->add('select', new Expr\Select($selects), true);
  485. }
  486. /**
  487. * Turns the query being built into a bulk delete query that ranges over
  488. * a certain entity type.
  489. *
  490. * <code>
  491. * $qb = $em->createQueryBuilder()
  492. * ->delete('User', 'u')
  493. * ->where('u.id = :user_id');
  494. * ->setParameter('user_id', 1);
  495. * </code>
  496. *
  497. * @param string $delete The class/type whose instances are subject to the deletion.
  498. * @param string $alias The class/type alias used in the constructed query.
  499. * @return QueryBuilder This QueryBuilder instance.
  500. */
  501. public function delete($delete = null, $alias = null)
  502. {
  503. $this->_type = self::DELETE;
  504. if ( ! $delete) {
  505. return $this;
  506. }
  507. return $this->add('from', new Expr\From($delete, $alias));
  508. }
  509. /**
  510. * Turns the query being built into a bulk update query that ranges over
  511. * a certain entity type.
  512. *
  513. * <code>
  514. * $qb = $em->createQueryBuilder()
  515. * ->update('User', 'u')
  516. * ->set('u.password', md5('password'))
  517. * ->where('u.id = ?');
  518. * </code>
  519. *
  520. * @param string $update The class/type whose instances are subject to the update.
  521. * @param string $alias The class/type alias used in the constructed query.
  522. * @return QueryBuilder This QueryBuilder instance.
  523. */
  524. public function update($update = null, $alias = null)
  525. {
  526. $this->_type = self::UPDATE;
  527. if ( ! $update) {
  528. return $this;
  529. }
  530. return $this->add('from', new Expr\From($update, $alias));
  531. }
  532. /**
  533. * Create and add a query root corresponding to the entity identified by the given alias,
  534. * forming a cartesian product with any existing query roots.
  535. *
  536. * <code>
  537. * $qb = $em->createQueryBuilder()
  538. * ->select('u')
  539. * ->from('User', 'u')
  540. * </code>
  541. *
  542. * @param string $from The class name.
  543. * @param string $alias The alias of the class.
  544. * @param string $indexBy The index for the from.
  545. * @return QueryBuilder This QueryBuilder instance.
  546. */
  547. public function from($from, $alias, $indexBy = null)
  548. {
  549. return $this->add('from', new Expr\From($from, $alias, $indexBy), true);
  550. }
  551. /**
  552. * Creates and adds a join over an entity association to the query.
  553. *
  554. * The entities in the joined association will be fetched as part of the query
  555. * result if the alias used for the joined association is placed in the select
  556. * expressions.
  557. *
  558. * <code>
  559. * $qb = $em->createQueryBuilder()
  560. * ->select('u')
  561. * ->from('User', 'u')
  562. * ->join('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
  563. * </code>
  564. *
  565. * @param string $join The relationship to join
  566. * @param string $alias The alias of the join
  567. * @param string $conditionType The condition type constant. Either ON or WITH.
  568. * @param string $condition The condition for the join
  569. * @param string $indexBy The index for the join
  570. * @return QueryBuilder This QueryBuilder instance.
  571. */
  572. public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
  573. {
  574. return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy);
  575. }
  576. /**
  577. * Creates and adds a join over an entity association to the query.
  578. *
  579. * The entities in the joined association will be fetched as part of the query
  580. * result if the alias used for the joined association is placed in the select
  581. * expressions.
  582. *
  583. * [php]
  584. * $qb = $em->createQueryBuilder()
  585. * ->select('u')
  586. * ->from('User', 'u')
  587. * ->innerJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
  588. *
  589. * @param string $join The relationship to join
  590. * @param string $alias The alias of the join
  591. * @param string $conditionType The condition type constant. Either ON or WITH.
  592. * @param string $condition The condition for the join
  593. * @param string $indexBy The index for the join
  594. * @return QueryBuilder This QueryBuilder instance.
  595. */
  596. public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
  597. {
  598. $rootAlias = substr($join, 0, strpos($join, '.'));
  599. if ( ! in_array($rootAlias, $this->getRootAliases())) {
  600. $rootAlias = $this->getRootAlias();
  601. }
  602. $join = new Expr\Join(
  603. Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy
  604. );
  605. return $this->add('join', array($rootAlias => $join), true);
  606. }
  607. /**
  608. * Creates and adds a left join over an entity association to the query.
  609. *
  610. * The entities in the joined association will be fetched as part of the query
  611. * result if the alias used for the joined association is placed in the select
  612. * expressions.
  613. *
  614. * <code>
  615. * $qb = $em->createQueryBuilder()
  616. * ->select('u')
  617. * ->from('User', 'u')
  618. * ->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, 'p.is_primary = 1');
  619. * </code>
  620. *
  621. * @param string $join The relationship to join
  622. * @param string $alias The alias of the join
  623. * @param string $conditionType The condition type constant. Either ON or WITH.
  624. * @param string $condition The condition for the join
  625. * @param string $indexBy The index for the join
  626. * @return QueryBuilder This QueryBuilder instance.
  627. */
  628. public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null)
  629. {
  630. $rootAlias = substr($join, 0, strpos($join, '.'));
  631. if ( ! in_array($rootAlias, $this->getRootAliases())) {
  632. $rootAlias = $this->getRootAlias();
  633. }
  634. $join = new Expr\Join(
  635. Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy
  636. );
  637. return $this->add('join', array($rootAlias => $join), 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. }