QueryBuilder.php 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412
  1. <?php
  2. namespace Doctrine\DBAL\Query;
  3. use Doctrine\DBAL\Connection;
  4. use Doctrine\DBAL\Driver\ResultStatement;
  5. use Doctrine\DBAL\Exception;
  6. use Doctrine\DBAL\ParameterType;
  7. use Doctrine\DBAL\Query\Expression\CompositeExpression;
  8. use Doctrine\DBAL\Query\Expression\ExpressionBuilder;
  9. use Doctrine\DBAL\Types\Type;
  10. use function array_filter;
  11. use function array_key_exists;
  12. use function array_keys;
  13. use function array_unshift;
  14. use function count;
  15. use function func_get_args;
  16. use function func_num_args;
  17. use function implode;
  18. use function is_array;
  19. use function is_object;
  20. use function key;
  21. use function strtoupper;
  22. use function substr;
  23. /**
  24. * QueryBuilder class is responsible to dynamically create SQL queries.
  25. *
  26. * Important: Verify that every feature you use will work with your database vendor.
  27. * SQL Query Builder does not attempt to validate the generated SQL at all.
  28. *
  29. * The query builder does no validation whatsoever if certain features even work with the
  30. * underlying database vendor. Limit queries and joins are NOT applied to UPDATE and DELETE statements
  31. * even if some vendors such as MySQL support it.
  32. */
  33. class QueryBuilder
  34. {
  35. /*
  36. * The query types.
  37. */
  38. public const SELECT = 0;
  39. public const DELETE = 1;
  40. public const UPDATE = 2;
  41. public const INSERT = 3;
  42. /*
  43. * The builder states.
  44. */
  45. public const STATE_DIRTY = 0;
  46. public const STATE_CLEAN = 1;
  47. /**
  48. * The DBAL Connection.
  49. *
  50. * @var Connection
  51. */
  52. private $connection;
  53. /*
  54. * The default values of SQL parts collection
  55. */
  56. private const SQL_PARTS_DEFAULTS = [
  57. 'select' => [],
  58. 'distinct' => false,
  59. 'from' => [],
  60. 'join' => [],
  61. 'set' => [],
  62. 'where' => null,
  63. 'groupBy' => [],
  64. 'having' => null,
  65. 'orderBy' => [],
  66. 'values' => [],
  67. ];
  68. /**
  69. * The array of SQL parts collected.
  70. *
  71. * @var mixed[]
  72. */
  73. private $sqlParts = self::SQL_PARTS_DEFAULTS;
  74. /**
  75. * The complete SQL string for this query.
  76. *
  77. * @var string
  78. */
  79. private $sql;
  80. /**
  81. * The query parameters.
  82. *
  83. * @var array<int, mixed>|array<string, mixed>
  84. */
  85. private $params = [];
  86. /**
  87. * The parameter type map of this query.
  88. *
  89. * @var array<int, int|string|Type|null>|array<string, int|string|Type|null>
  90. */
  91. private $paramTypes = [];
  92. /**
  93. * The type of query this is. Can be select, update or delete.
  94. *
  95. * @var int
  96. */
  97. private $type = self::SELECT;
  98. /**
  99. * The state of the query object. Can be dirty or clean.
  100. *
  101. * @var int
  102. */
  103. private $state = self::STATE_CLEAN;
  104. /**
  105. * The index of the first result to retrieve.
  106. *
  107. * @var int
  108. */
  109. private $firstResult;
  110. /**
  111. * The maximum number of results to retrieve or NULL to retrieve all results.
  112. *
  113. * @var int|null
  114. */
  115. private $maxResults;
  116. /**
  117. * The counter of bound parameters used with {@see bindValue).
  118. *
  119. * @var int
  120. */
  121. private $boundCounter = 0;
  122. /**
  123. * Initializes a new <tt>QueryBuilder</tt>.
  124. *
  125. * @param Connection $connection The DBAL Connection.
  126. */
  127. public function __construct(Connection $connection)
  128. {
  129. $this->connection = $connection;
  130. }
  131. /**
  132. * Gets an ExpressionBuilder used for object-oriented construction of query expressions.
  133. * This producer method is intended for convenient inline usage. Example:
  134. *
  135. * <code>
  136. * $qb = $conn->createQueryBuilder()
  137. * ->select('u')
  138. * ->from('users', 'u')
  139. * ->where($qb->expr()->eq('u.id', 1));
  140. * </code>
  141. *
  142. * For more complex expression construction, consider storing the expression
  143. * builder object in a local variable.
  144. *
  145. * @return ExpressionBuilder
  146. */
  147. public function expr()
  148. {
  149. return $this->connection->getExpressionBuilder();
  150. }
  151. /**
  152. * Gets the type of the currently built query.
  153. *
  154. * @return int
  155. */
  156. public function getType()
  157. {
  158. return $this->type;
  159. }
  160. /**
  161. * Gets the associated DBAL Connection for this query builder.
  162. *
  163. * @return Connection
  164. */
  165. public function getConnection()
  166. {
  167. return $this->connection;
  168. }
  169. /**
  170. * Gets the state of this query builder instance.
  171. *
  172. * @return int Either QueryBuilder::STATE_DIRTY or QueryBuilder::STATE_CLEAN.
  173. */
  174. public function getState()
  175. {
  176. return $this->state;
  177. }
  178. /**
  179. * Executes this query using the bound parameters and their types.
  180. *
  181. * @return ResultStatement|int
  182. *
  183. * @throws Exception
  184. */
  185. public function execute()
  186. {
  187. if ($this->type === self::SELECT) {
  188. return $this->connection->executeQuery($this->getSQL(), $this->params, $this->paramTypes);
  189. }
  190. return $this->connection->executeStatement($this->getSQL(), $this->params, $this->paramTypes);
  191. }
  192. /**
  193. * Gets the complete SQL string formed by the current specifications of this QueryBuilder.
  194. *
  195. * <code>
  196. * $qb = $em->createQueryBuilder()
  197. * ->select('u')
  198. * ->from('User', 'u')
  199. * echo $qb->getSQL(); // SELECT u FROM User u
  200. * </code>
  201. *
  202. * @return string The SQL query string.
  203. */
  204. public function getSQL()
  205. {
  206. if ($this->sql !== null && $this->state === self::STATE_CLEAN) {
  207. return $this->sql;
  208. }
  209. switch ($this->type) {
  210. case self::INSERT:
  211. $sql = $this->getSQLForInsert();
  212. break;
  213. case self::DELETE:
  214. $sql = $this->getSQLForDelete();
  215. break;
  216. case self::UPDATE:
  217. $sql = $this->getSQLForUpdate();
  218. break;
  219. case self::SELECT:
  220. default:
  221. $sql = $this->getSQLForSelect();
  222. break;
  223. }
  224. $this->state = self::STATE_CLEAN;
  225. $this->sql = $sql;
  226. return $sql;
  227. }
  228. /**
  229. * Sets a query parameter for the query being constructed.
  230. *
  231. * <code>
  232. * $qb = $conn->createQueryBuilder()
  233. * ->select('u')
  234. * ->from('users', 'u')
  235. * ->where('u.id = :user_id')
  236. * ->setParameter(':user_id', 1);
  237. * </code>
  238. *
  239. * @param int|string $key Parameter position or name
  240. * @param mixed $value Parameter value
  241. * @param int|string|Type|null $type One of the {@link ParameterType} constants or DBAL type
  242. *
  243. * @return $this This QueryBuilder instance.
  244. */
  245. public function setParameter($key, $value, $type = null)
  246. {
  247. if ($type !== null) {
  248. $this->paramTypes[$key] = $type;
  249. }
  250. $this->params[$key] = $value;
  251. return $this;
  252. }
  253. /**
  254. * Sets a collection of query parameters for the query being constructed.
  255. *
  256. * <code>
  257. * $qb = $conn->createQueryBuilder()
  258. * ->select('u')
  259. * ->from('users', 'u')
  260. * ->where('u.id = :user_id1 OR u.id = :user_id2')
  261. * ->setParameters(array(
  262. * ':user_id1' => 1,
  263. * ':user_id2' => 2
  264. * ));
  265. * </code>
  266. *
  267. * @param array<int, mixed>|array<string, mixed> $params Parameters to set
  268. * @param array<int, int|string|Type|null>|array<string, int|string|Type|null> $types Parameter types
  269. *
  270. * @return $this This QueryBuilder instance.
  271. */
  272. public function setParameters(array $params, array $types = [])
  273. {
  274. $this->paramTypes = $types;
  275. $this->params = $params;
  276. return $this;
  277. }
  278. /**
  279. * Gets all defined query parameters for the query being constructed indexed by parameter index or name.
  280. *
  281. * @return array<int, mixed>|array<string, mixed> The currently defined query parameters
  282. */
  283. public function getParameters()
  284. {
  285. return $this->params;
  286. }
  287. /**
  288. * Gets a (previously set) query parameter of the query being constructed.
  289. *
  290. * @param mixed $key The key (index or name) of the bound parameter.
  291. *
  292. * @return mixed The value of the bound parameter.
  293. */
  294. public function getParameter($key)
  295. {
  296. return $this->params[$key] ?? null;
  297. }
  298. /**
  299. * Gets all defined query parameter types for the query being constructed indexed by parameter index or name.
  300. *
  301. * @return array<int, int|string|Type|null>|array<string, int|string|Type|null> The currently defined
  302. * query parameter types
  303. */
  304. public function getParameterTypes()
  305. {
  306. return $this->paramTypes;
  307. }
  308. /**
  309. * Gets a (previously set) query parameter type of the query being constructed.
  310. *
  311. * @param int|string $key The key of the bound parameter type
  312. *
  313. * @return int|string|Type|null The value of the bound parameter type
  314. */
  315. public function getParameterType($key)
  316. {
  317. return $this->paramTypes[$key] ?? null;
  318. }
  319. /**
  320. * Sets the position of the first result to retrieve (the "offset").
  321. *
  322. * @param int $firstResult The first result to return.
  323. *
  324. * @return $this This QueryBuilder instance.
  325. */
  326. public function setFirstResult($firstResult)
  327. {
  328. $this->state = self::STATE_DIRTY;
  329. $this->firstResult = $firstResult;
  330. return $this;
  331. }
  332. /**
  333. * Gets the position of the first result the query object was set to retrieve (the "offset").
  334. *
  335. * @return int The position of the first result.
  336. */
  337. public function getFirstResult()
  338. {
  339. return $this->firstResult;
  340. }
  341. /**
  342. * Sets the maximum number of results to retrieve (the "limit").
  343. *
  344. * @param int|null $maxResults The maximum number of results to retrieve or NULL to retrieve all results.
  345. *
  346. * @return $this This QueryBuilder instance.
  347. */
  348. public function setMaxResults($maxResults)
  349. {
  350. $this->state = self::STATE_DIRTY;
  351. $this->maxResults = $maxResults;
  352. return $this;
  353. }
  354. /**
  355. * Gets the maximum number of results the query object was set to retrieve (the "limit").
  356. * Returns NULL if all results will be returned.
  357. *
  358. * @return int|null The maximum number of results.
  359. */
  360. public function getMaxResults()
  361. {
  362. return $this->maxResults;
  363. }
  364. /**
  365. * Either appends to or replaces a single, generic query part.
  366. *
  367. * The available parts are: 'select', 'from', 'set', 'where',
  368. * 'groupBy', 'having' and 'orderBy'.
  369. *
  370. * @param string $sqlPartName
  371. * @param mixed $sqlPart
  372. * @param bool $append
  373. *
  374. * @return $this This QueryBuilder instance.
  375. */
  376. public function add($sqlPartName, $sqlPart, $append = false)
  377. {
  378. $isArray = is_array($sqlPart);
  379. $isMultiple = is_array($this->sqlParts[$sqlPartName]);
  380. if ($isMultiple && ! $isArray) {
  381. $sqlPart = [$sqlPart];
  382. }
  383. $this->state = self::STATE_DIRTY;
  384. if ($append) {
  385. if (
  386. $sqlPartName === 'orderBy'
  387. || $sqlPartName === 'groupBy'
  388. || $sqlPartName === 'select'
  389. || $sqlPartName === 'set'
  390. ) {
  391. foreach ($sqlPart as $part) {
  392. $this->sqlParts[$sqlPartName][] = $part;
  393. }
  394. } elseif ($isArray && is_array($sqlPart[key($sqlPart)])) {
  395. $key = key($sqlPart);
  396. $this->sqlParts[$sqlPartName][$key][] = $sqlPart[$key];
  397. } elseif ($isMultiple) {
  398. $this->sqlParts[$sqlPartName][] = $sqlPart;
  399. } else {
  400. $this->sqlParts[$sqlPartName] = $sqlPart;
  401. }
  402. return $this;
  403. }
  404. $this->sqlParts[$sqlPartName] = $sqlPart;
  405. return $this;
  406. }
  407. /**
  408. * Specifies an item that is to be returned in the query result.
  409. * Replaces any previously specified selections, if any.
  410. *
  411. * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  412. *
  413. * <code>
  414. * $qb = $conn->createQueryBuilder()
  415. * ->select('u.id', 'p.id')
  416. * ->from('users', 'u')
  417. * ->leftJoin('u', 'phonenumbers', 'p', 'u.id = p.user_id');
  418. * </code>
  419. *
  420. * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED.
  421. * Pass each value as an individual argument.
  422. *
  423. * @return $this This QueryBuilder instance.
  424. */
  425. public function select($select = null/*, string ...$selects*/)
  426. {
  427. $this->type = self::SELECT;
  428. if (empty($select)) {
  429. return $this;
  430. }
  431. $selects = is_array($select) ? $select : func_get_args();
  432. return $this->add('select', $selects);
  433. }
  434. /**
  435. * Adds DISTINCT to the query.
  436. *
  437. * <code>
  438. * $qb = $conn->createQueryBuilder()
  439. * ->select('u.id')
  440. * ->distinct()
  441. * ->from('users', 'u')
  442. * </code>
  443. *
  444. * @return $this This QueryBuilder instance.
  445. */
  446. public function distinct(): self
  447. {
  448. $this->sqlParts['distinct'] = true;
  449. return $this;
  450. }
  451. /**
  452. * Adds an item that is to be returned in the query result.
  453. *
  454. * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  455. *
  456. * <code>
  457. * $qb = $conn->createQueryBuilder()
  458. * ->select('u.id')
  459. * ->addSelect('p.id')
  460. * ->from('users', 'u')
  461. * ->leftJoin('u', 'phonenumbers', 'u.id = p.user_id');
  462. * </code>
  463. *
  464. * @param string|string[]|null $select The selection expression. USING AN ARRAY OR NULL IS DEPRECATED.
  465. * Pass each value as an individual argument.
  466. *
  467. * @return $this This QueryBuilder instance.
  468. */
  469. public function addSelect($select = null/*, string ...$selects*/)
  470. {
  471. $this->type = self::SELECT;
  472. if (empty($select)) {
  473. return $this;
  474. }
  475. $selects = is_array($select) ? $select : func_get_args();
  476. return $this->add('select', $selects, true);
  477. }
  478. /**
  479. * Turns the query being built into a bulk delete query that ranges over
  480. * a certain table.
  481. *
  482. * <code>
  483. * $qb = $conn->createQueryBuilder()
  484. * ->delete('users', 'u')
  485. * ->where('u.id = :user_id')
  486. * ->setParameter(':user_id', 1);
  487. * </code>
  488. *
  489. * @param string $delete The table whose rows are subject to the deletion.
  490. * @param string $alias The table alias used in the constructed query.
  491. *
  492. * @return $this This QueryBuilder instance.
  493. */
  494. public function delete($delete = null, $alias = null)
  495. {
  496. $this->type = self::DELETE;
  497. if (! $delete) {
  498. return $this;
  499. }
  500. return $this->add('from', [
  501. 'table' => $delete,
  502. 'alias' => $alias,
  503. ]);
  504. }
  505. /**
  506. * Turns the query being built into a bulk update query that ranges over
  507. * a certain table
  508. *
  509. * <code>
  510. * $qb = $conn->createQueryBuilder()
  511. * ->update('counters', 'c')
  512. * ->set('c.value', 'c.value + 1')
  513. * ->where('c.id = ?');
  514. * </code>
  515. *
  516. * @param string $update The table whose rows are subject to the update.
  517. * @param string $alias The table alias used in the constructed query.
  518. *
  519. * @return $this This QueryBuilder instance.
  520. */
  521. public function update($update = null, $alias = null)
  522. {
  523. $this->type = self::UPDATE;
  524. if (! $update) {
  525. return $this;
  526. }
  527. return $this->add('from', [
  528. 'table' => $update,
  529. 'alias' => $alias,
  530. ]);
  531. }
  532. /**
  533. * Turns the query being built into an insert query that inserts into
  534. * a certain table
  535. *
  536. * <code>
  537. * $qb = $conn->createQueryBuilder()
  538. * ->insert('users')
  539. * ->values(
  540. * array(
  541. * 'name' => '?',
  542. * 'password' => '?'
  543. * )
  544. * );
  545. * </code>
  546. *
  547. * @param string $insert The table into which the rows should be inserted.
  548. *
  549. * @return $this This QueryBuilder instance.
  550. */
  551. public function insert($insert = null)
  552. {
  553. $this->type = self::INSERT;
  554. if (! $insert) {
  555. return $this;
  556. }
  557. return $this->add('from', ['table' => $insert]);
  558. }
  559. /**
  560. * Creates and adds a query root corresponding to the table identified by the
  561. * given alias, forming a cartesian product with any existing query roots.
  562. *
  563. * <code>
  564. * $qb = $conn->createQueryBuilder()
  565. * ->select('u.id')
  566. * ->from('users', 'u')
  567. * </code>
  568. *
  569. * @param string $from The table.
  570. * @param string|null $alias The alias of the table.
  571. *
  572. * @return $this This QueryBuilder instance.
  573. */
  574. public function from($from, $alias = null)
  575. {
  576. return $this->add('from', [
  577. 'table' => $from,
  578. 'alias' => $alias,
  579. ], true);
  580. }
  581. /**
  582. * Creates and adds a join to the query.
  583. *
  584. * <code>
  585. * $qb = $conn->createQueryBuilder()
  586. * ->select('u.name')
  587. * ->from('users', 'u')
  588. * ->join('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  589. * </code>
  590. *
  591. * @param string $fromAlias The alias that points to a from clause.
  592. * @param string $join The table name to join.
  593. * @param string $alias The alias of the join table.
  594. * @param string $condition The condition for the join.
  595. *
  596. * @return $this This QueryBuilder instance.
  597. */
  598. public function join($fromAlias, $join, $alias, $condition = null)
  599. {
  600. return $this->innerJoin($fromAlias, $join, $alias, $condition);
  601. }
  602. /**
  603. * Creates and adds a join to the query.
  604. *
  605. * <code>
  606. * $qb = $conn->createQueryBuilder()
  607. * ->select('u.name')
  608. * ->from('users', 'u')
  609. * ->innerJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  610. * </code>
  611. *
  612. * @param string $fromAlias The alias that points to a from clause.
  613. * @param string $join The table name to join.
  614. * @param string $alias The alias of the join table.
  615. * @param string $condition The condition for the join.
  616. *
  617. * @return $this This QueryBuilder instance.
  618. */
  619. public function innerJoin($fromAlias, $join, $alias, $condition = null)
  620. {
  621. return $this->add('join', [
  622. $fromAlias => [
  623. 'joinType' => 'inner',
  624. 'joinTable' => $join,
  625. 'joinAlias' => $alias,
  626. 'joinCondition' => $condition,
  627. ],
  628. ], true);
  629. }
  630. /**
  631. * Creates and adds a left join to the query.
  632. *
  633. * <code>
  634. * $qb = $conn->createQueryBuilder()
  635. * ->select('u.name')
  636. * ->from('users', 'u')
  637. * ->leftJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  638. * </code>
  639. *
  640. * @param string $fromAlias The alias that points to a from clause.
  641. * @param string $join The table name to join.
  642. * @param string $alias The alias of the join table.
  643. * @param string $condition The condition for the join.
  644. *
  645. * @return $this This QueryBuilder instance.
  646. */
  647. public function leftJoin($fromAlias, $join, $alias, $condition = null)
  648. {
  649. return $this->add('join', [
  650. $fromAlias => [
  651. 'joinType' => 'left',
  652. 'joinTable' => $join,
  653. 'joinAlias' => $alias,
  654. 'joinCondition' => $condition,
  655. ],
  656. ], true);
  657. }
  658. /**
  659. * Creates and adds a right join to the query.
  660. *
  661. * <code>
  662. * $qb = $conn->createQueryBuilder()
  663. * ->select('u.name')
  664. * ->from('users', 'u')
  665. * ->rightJoin('u', 'phonenumbers', 'p', 'p.is_primary = 1');
  666. * </code>
  667. *
  668. * @param string $fromAlias The alias that points to a from clause.
  669. * @param string $join The table name to join.
  670. * @param string $alias The alias of the join table.
  671. * @param string $condition The condition for the join.
  672. *
  673. * @return $this This QueryBuilder instance.
  674. */
  675. public function rightJoin($fromAlias, $join, $alias, $condition = null)
  676. {
  677. return $this->add('join', [
  678. $fromAlias => [
  679. 'joinType' => 'right',
  680. 'joinTable' => $join,
  681. 'joinAlias' => $alias,
  682. 'joinCondition' => $condition,
  683. ],
  684. ], true);
  685. }
  686. /**
  687. * Sets a new value for a column in a bulk update query.
  688. *
  689. * <code>
  690. * $qb = $conn->createQueryBuilder()
  691. * ->update('counters', 'c')
  692. * ->set('c.value', 'c.value + 1')
  693. * ->where('c.id = ?');
  694. * </code>
  695. *
  696. * @param string $key The column to set.
  697. * @param string $value The value, expression, placeholder, etc.
  698. *
  699. * @return $this This QueryBuilder instance.
  700. */
  701. public function set($key, $value)
  702. {
  703. return $this->add('set', $key . ' = ' . $value, true);
  704. }
  705. /**
  706. * Specifies one or more restrictions to the query result.
  707. * Replaces any previously specified restrictions, if any.
  708. *
  709. * <code>
  710. * $qb = $conn->createQueryBuilder()
  711. * ->select('c.value')
  712. * ->from('counters', 'c')
  713. * ->where('c.id = ?');
  714. *
  715. * // You can optionally programatically build and/or expressions
  716. * $qb = $conn->createQueryBuilder();
  717. *
  718. * $or = $qb->expr()->orx();
  719. * $or->add($qb->expr()->eq('c.id', 1));
  720. * $or->add($qb->expr()->eq('c.id', 2));
  721. *
  722. * $qb->update('counters', 'c')
  723. * ->set('c.value', 'c.value + 1')
  724. * ->where($or);
  725. * </code>
  726. *
  727. * @param mixed $predicates The restriction predicates.
  728. *
  729. * @return $this This QueryBuilder instance.
  730. */
  731. public function where($predicates)
  732. {
  733. if (! (func_num_args() === 1 && $predicates instanceof CompositeExpression)) {
  734. $predicates = CompositeExpression::and(...func_get_args());
  735. }
  736. return $this->add('where', $predicates);
  737. }
  738. /**
  739. * Adds one or more restrictions to the query results, forming a logical
  740. * conjunction with any previously specified restrictions.
  741. *
  742. * <code>
  743. * $qb = $conn->createQueryBuilder()
  744. * ->select('u')
  745. * ->from('users', 'u')
  746. * ->where('u.username LIKE ?')
  747. * ->andWhere('u.is_active = 1');
  748. * </code>
  749. *
  750. * @see where()
  751. *
  752. * @param mixed $where The query restrictions.
  753. *
  754. * @return $this This QueryBuilder instance.
  755. */
  756. public function andWhere($where)
  757. {
  758. $args = func_get_args();
  759. $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  760. $where = $this->getQueryPart('where');
  761. if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_AND) {
  762. if (count($args) > 0) {
  763. $where = $where->with(...$args);
  764. }
  765. } else {
  766. array_unshift($args, $where);
  767. $where = CompositeExpression::and(...$args);
  768. }
  769. return $this->add('where', $where, true);
  770. }
  771. /**
  772. * Adds one or more restrictions to the query results, forming a logical
  773. * disjunction with any previously specified restrictions.
  774. *
  775. * <code>
  776. * $qb = $em->createQueryBuilder()
  777. * ->select('u.name')
  778. * ->from('users', 'u')
  779. * ->where('u.id = 1')
  780. * ->orWhere('u.id = 2');
  781. * </code>
  782. *
  783. * @see where()
  784. *
  785. * @param mixed $where The WHERE statement.
  786. *
  787. * @return $this This QueryBuilder instance.
  788. */
  789. public function orWhere($where)
  790. {
  791. $args = func_get_args();
  792. $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  793. $where = $this->getQueryPart('where');
  794. if ($where instanceof CompositeExpression && $where->getType() === CompositeExpression::TYPE_OR) {
  795. if (count($args) > 0) {
  796. $where = $where->with(...$args);
  797. }
  798. } else {
  799. array_unshift($args, $where);
  800. $where = CompositeExpression::or(...$args);
  801. }
  802. return $this->add('where', $where, true);
  803. }
  804. /**
  805. * Specifies a grouping over the results of the query.
  806. * Replaces any previously specified groupings, if any.
  807. *
  808. * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  809. *
  810. * <code>
  811. * $qb = $conn->createQueryBuilder()
  812. * ->select('u.name')
  813. * ->from('users', 'u')
  814. * ->groupBy('u.id');
  815. * </code>
  816. *
  817. * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED.
  818. * Pass each value as an individual argument.
  819. *
  820. * @return $this This QueryBuilder instance.
  821. */
  822. public function groupBy($groupBy/*, string ...$groupBys*/)
  823. {
  824. if (empty($groupBy)) {
  825. return $this;
  826. }
  827. $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
  828. return $this->add('groupBy', $groupBy, false);
  829. }
  830. /**
  831. * Adds a grouping expression to the query.
  832. *
  833. * USING AN ARRAY ARGUMENT IS DEPRECATED. Pass each value as an individual argument.
  834. *
  835. * <code>
  836. * $qb = $conn->createQueryBuilder()
  837. * ->select('u.name')
  838. * ->from('users', 'u')
  839. * ->groupBy('u.lastLogin')
  840. * ->addGroupBy('u.createdAt');
  841. * </code>
  842. *
  843. * @param string|string[] $groupBy The grouping expression. USING AN ARRAY IS DEPRECATED.
  844. * Pass each value as an individual argument.
  845. *
  846. * @return $this This QueryBuilder instance.
  847. */
  848. public function addGroupBy($groupBy/*, string ...$groupBys*/)
  849. {
  850. if (empty($groupBy)) {
  851. return $this;
  852. }
  853. $groupBy = is_array($groupBy) ? $groupBy : func_get_args();
  854. return $this->add('groupBy', $groupBy, true);
  855. }
  856. /**
  857. * Sets a value for a column in an insert query.
  858. *
  859. * <code>
  860. * $qb = $conn->createQueryBuilder()
  861. * ->insert('users')
  862. * ->values(
  863. * array(
  864. * 'name' => '?'
  865. * )
  866. * )
  867. * ->setValue('password', '?');
  868. * </code>
  869. *
  870. * @param string $column The column into which the value should be inserted.
  871. * @param string $value The value that should be inserted into the column.
  872. *
  873. * @return $this This QueryBuilder instance.
  874. */
  875. public function setValue($column, $value)
  876. {
  877. $this->sqlParts['values'][$column] = $value;
  878. return $this;
  879. }
  880. /**
  881. * Specifies values for an insert query indexed by column names.
  882. * Replaces any previous values, if any.
  883. *
  884. * <code>
  885. * $qb = $conn->createQueryBuilder()
  886. * ->insert('users')
  887. * ->values(
  888. * array(
  889. * 'name' => '?',
  890. * 'password' => '?'
  891. * )
  892. * );
  893. * </code>
  894. *
  895. * @param mixed[] $values The values to specify for the insert query indexed by column names.
  896. *
  897. * @return $this This QueryBuilder instance.
  898. */
  899. public function values(array $values)
  900. {
  901. return $this->add('values', $values);
  902. }
  903. /**
  904. * Specifies a restriction over the groups of the query.
  905. * Replaces any previous having restrictions, if any.
  906. *
  907. * @param mixed $having The restriction over the groups.
  908. *
  909. * @return $this This QueryBuilder instance.
  910. */
  911. public function having($having)
  912. {
  913. if (! (func_num_args() === 1 && $having instanceof CompositeExpression)) {
  914. $having = CompositeExpression::and(...func_get_args());
  915. }
  916. return $this->add('having', $having);
  917. }
  918. /**
  919. * Adds a restriction over the groups of the query, forming a logical
  920. * conjunction with any existing having restrictions.
  921. *
  922. * @param mixed $having The restriction to append.
  923. *
  924. * @return $this This QueryBuilder instance.
  925. */
  926. public function andHaving($having)
  927. {
  928. $args = func_get_args();
  929. $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  930. $having = $this->getQueryPart('having');
  931. if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_AND) {
  932. $having = $having->with(...$args);
  933. } else {
  934. array_unshift($args, $having);
  935. $having = CompositeExpression::and(...$args);
  936. }
  937. return $this->add('having', $having);
  938. }
  939. /**
  940. * Adds a restriction over the groups of the query, forming a logical
  941. * disjunction with any existing having restrictions.
  942. *
  943. * @param mixed $having The restriction to add.
  944. *
  945. * @return $this This QueryBuilder instance.
  946. */
  947. public function orHaving($having)
  948. {
  949. $args = func_get_args();
  950. $args = array_filter($args); // https://github.com/doctrine/dbal/issues/4282
  951. $having = $this->getQueryPart('having');
  952. if ($having instanceof CompositeExpression && $having->getType() === CompositeExpression::TYPE_OR) {
  953. $having = $having->with(...$args);
  954. } else {
  955. array_unshift($args, $having);
  956. $having = CompositeExpression::or(...$args);
  957. }
  958. return $this->add('having', $having);
  959. }
  960. /**
  961. * Specifies an ordering for the query results.
  962. * Replaces any previously specified orderings, if any.
  963. *
  964. * @param string $sort The ordering expression.
  965. * @param string $order The ordering direction.
  966. *
  967. * @return $this This QueryBuilder instance.
  968. */
  969. public function orderBy($sort, $order = null)
  970. {
  971. return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), false);
  972. }
  973. /**
  974. * Adds an ordering to the query results.
  975. *
  976. * @param string $sort The ordering expression.
  977. * @param string $order The ordering direction.
  978. *
  979. * @return $this This QueryBuilder instance.
  980. */
  981. public function addOrderBy($sort, $order = null)
  982. {
  983. return $this->add('orderBy', $sort . ' ' . (! $order ? 'ASC' : $order), true);
  984. }
  985. /**
  986. * Gets a query part by its name.
  987. *
  988. * @param string $queryPartName
  989. *
  990. * @return mixed
  991. */
  992. public function getQueryPart($queryPartName)
  993. {
  994. return $this->sqlParts[$queryPartName];
  995. }
  996. /**
  997. * Gets all query parts.
  998. *
  999. * @return mixed[]
  1000. */
  1001. public function getQueryParts()
  1002. {
  1003. return $this->sqlParts;
  1004. }
  1005. /**
  1006. * Resets SQL parts.
  1007. *
  1008. * @param string[]|null $queryPartNames
  1009. *
  1010. * @return $this This QueryBuilder instance.
  1011. */
  1012. public function resetQueryParts($queryPartNames = null)
  1013. {
  1014. if ($queryPartNames === null) {
  1015. $queryPartNames = array_keys($this->sqlParts);
  1016. }
  1017. foreach ($queryPartNames as $queryPartName) {
  1018. $this->resetQueryPart($queryPartName);
  1019. }
  1020. return $this;
  1021. }
  1022. /**
  1023. * Resets a single SQL part.
  1024. *
  1025. * @param string $queryPartName
  1026. *
  1027. * @return $this This QueryBuilder instance.
  1028. */
  1029. public function resetQueryPart($queryPartName)
  1030. {
  1031. $this->sqlParts[$queryPartName] = self::SQL_PARTS_DEFAULTS[$queryPartName];
  1032. $this->state = self::STATE_DIRTY;
  1033. return $this;
  1034. }
  1035. /**
  1036. * @return string
  1037. *
  1038. * @throws QueryException
  1039. */
  1040. private function getSQLForSelect()
  1041. {
  1042. $query = 'SELECT ' . ($this->sqlParts['distinct'] ? 'DISTINCT ' : '') .
  1043. implode(', ', $this->sqlParts['select']);
  1044. $query .= ($this->sqlParts['from'] ? ' FROM ' . implode(', ', $this->getFromClauses()) : '')
  1045. . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '')
  1046. . ($this->sqlParts['groupBy'] ? ' GROUP BY ' . implode(', ', $this->sqlParts['groupBy']) : '')
  1047. . ($this->sqlParts['having'] !== null ? ' HAVING ' . ((string) $this->sqlParts['having']) : '')
  1048. . ($this->sqlParts['orderBy'] ? ' ORDER BY ' . implode(', ', $this->sqlParts['orderBy']) : '');
  1049. if ($this->isLimitQuery()) {
  1050. return $this->connection->getDatabasePlatform()->modifyLimitQuery(
  1051. $query,
  1052. $this->maxResults,
  1053. $this->firstResult
  1054. );
  1055. }
  1056. return $query;
  1057. }
  1058. /**
  1059. * @return string[]
  1060. */
  1061. private function getFromClauses()
  1062. {
  1063. $fromClauses = [];
  1064. $knownAliases = [];
  1065. // Loop through all FROM clauses
  1066. foreach ($this->sqlParts['from'] as $from) {
  1067. if ($from['alias'] === null) {
  1068. $tableSql = $from['table'];
  1069. $tableReference = $from['table'];
  1070. } else {
  1071. $tableSql = $from['table'] . ' ' . $from['alias'];
  1072. $tableReference = $from['alias'];
  1073. }
  1074. $knownAliases[$tableReference] = true;
  1075. $fromClauses[$tableReference] = $tableSql . $this->getSQLForJoins($tableReference, $knownAliases);
  1076. }
  1077. $this->verifyAllAliasesAreKnown($knownAliases);
  1078. return $fromClauses;
  1079. }
  1080. /**
  1081. * @param array<string,true> $knownAliases
  1082. *
  1083. * @throws QueryException
  1084. */
  1085. private function verifyAllAliasesAreKnown(array $knownAliases): void
  1086. {
  1087. foreach ($this->sqlParts['join'] as $fromAlias => $joins) {
  1088. if (! isset($knownAliases[$fromAlias])) {
  1089. throw QueryException::unknownAlias($fromAlias, array_keys($knownAliases));
  1090. }
  1091. }
  1092. }
  1093. /**
  1094. * @return bool
  1095. */
  1096. private function isLimitQuery()
  1097. {
  1098. return $this->maxResults !== null || $this->firstResult !== null;
  1099. }
  1100. /**
  1101. * Converts this instance into an INSERT string in SQL.
  1102. *
  1103. * @return string
  1104. */
  1105. private function getSQLForInsert()
  1106. {
  1107. return 'INSERT INTO ' . $this->sqlParts['from']['table'] .
  1108. ' (' . implode(', ', array_keys($this->sqlParts['values'])) . ')' .
  1109. ' VALUES(' . implode(', ', $this->sqlParts['values']) . ')';
  1110. }
  1111. /**
  1112. * Converts this instance into an UPDATE string in SQL.
  1113. *
  1114. * @return string
  1115. */
  1116. private function getSQLForUpdate()
  1117. {
  1118. $table = $this->sqlParts['from']['table']
  1119. . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
  1120. return 'UPDATE ' . $table
  1121. . ' SET ' . implode(', ', $this->sqlParts['set'])
  1122. . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
  1123. }
  1124. /**
  1125. * Converts this instance into a DELETE string in SQL.
  1126. *
  1127. * @return string
  1128. */
  1129. private function getSQLForDelete()
  1130. {
  1131. $table = $this->sqlParts['from']['table']
  1132. . ($this->sqlParts['from']['alias'] ? ' ' . $this->sqlParts['from']['alias'] : '');
  1133. return 'DELETE FROM ' . $table
  1134. . ($this->sqlParts['where'] !== null ? ' WHERE ' . ((string) $this->sqlParts['where']) : '');
  1135. }
  1136. /**
  1137. * Gets a string representation of this QueryBuilder which corresponds to
  1138. * the final SQL query being constructed.
  1139. *
  1140. * @return string The string representation of this QueryBuilder.
  1141. */
  1142. public function __toString()
  1143. {
  1144. return $this->getSQL();
  1145. }
  1146. /**
  1147. * Creates a new named parameter and bind the value $value to it.
  1148. *
  1149. * This method provides a shortcut for PDOStatement::bindValue
  1150. * when using prepared statements.
  1151. *
  1152. * The parameter $value specifies the value that you want to bind. If
  1153. * $placeholder is not provided bindValue() will automatically create a
  1154. * placeholder for you. An automatic placeholder will be of the name
  1155. * ':dcValue1', ':dcValue2' etc.
  1156. *
  1157. * For more information see {@link http://php.net/pdostatement-bindparam}
  1158. *
  1159. * Example:
  1160. * <code>
  1161. * $value = 2;
  1162. * $q->eq( 'id', $q->bindValue( $value ) );
  1163. * $stmt = $q->executeQuery(); // executed with 'id = 2'
  1164. * </code>
  1165. *
  1166. * @link http://www.zetacomponents.org
  1167. *
  1168. * @param mixed $value
  1169. * @param int|string|Type|null $type
  1170. * @param string $placeHolder The name to bind with. The string must start with a colon ':'.
  1171. *
  1172. * @return string the placeholder name used.
  1173. */
  1174. public function createNamedParameter($value, $type = ParameterType::STRING, $placeHolder = null)
  1175. {
  1176. if ($placeHolder === null) {
  1177. $this->boundCounter++;
  1178. $placeHolder = ':dcValue' . $this->boundCounter;
  1179. }
  1180. $this->setParameter(substr($placeHolder, 1), $value, $type);
  1181. return $placeHolder;
  1182. }
  1183. /**
  1184. * Creates a new positional parameter and bind the given value to it.
  1185. *
  1186. * Attention: If you are using positional parameters with the query builder you have
  1187. * to be very careful to bind all parameters in the order they appear in the SQL
  1188. * statement , otherwise they get bound in the wrong order which can lead to serious
  1189. * bugs in your code.
  1190. *
  1191. * Example:
  1192. * <code>
  1193. * $qb = $conn->createQueryBuilder();
  1194. * $qb->select('u.*')
  1195. * ->from('users', 'u')
  1196. * ->where('u.username = ' . $qb->createPositionalParameter('Foo', ParameterType::STRING))
  1197. * ->orWhere('u.username = ' . $qb->createPositionalParameter('Bar', ParameterType::STRING))
  1198. * </code>
  1199. *
  1200. * @param mixed $value
  1201. * @param int|string|Type|null $type
  1202. *
  1203. * @return string
  1204. */
  1205. public function createPositionalParameter($value, $type = ParameterType::STRING)
  1206. {
  1207. $this->boundCounter++;
  1208. $this->setParameter($this->boundCounter, $value, $type);
  1209. return '?';
  1210. }
  1211. /**
  1212. * @param string $fromAlias
  1213. * @param array<string,true> $knownAliases
  1214. *
  1215. * @return string
  1216. *
  1217. * @throws QueryException
  1218. */
  1219. private function getSQLForJoins($fromAlias, array &$knownAliases)
  1220. {
  1221. $sql = '';
  1222. if (isset($this->sqlParts['join'][$fromAlias])) {
  1223. foreach ($this->sqlParts['join'][$fromAlias] as $join) {
  1224. if (array_key_exists($join['joinAlias'], $knownAliases)) {
  1225. throw QueryException::nonUniqueAlias($join['joinAlias'], array_keys($knownAliases));
  1226. }
  1227. $sql .= ' ' . strtoupper($join['joinType'])
  1228. . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'];
  1229. if ($join['joinCondition'] !== null) {
  1230. $sql .= ' ON ' . $join['joinCondition'];
  1231. }
  1232. $knownAliases[$join['joinAlias']] = true;
  1233. }
  1234. foreach ($this->sqlParts['join'][$fromAlias] as $join) {
  1235. $sql .= $this->getSQLForJoins($join['joinAlias'], $knownAliases);
  1236. }
  1237. }
  1238. return $sql;
  1239. }
  1240. /**
  1241. * Deep clone of all expression objects in the SQL parts.
  1242. *
  1243. * @return void
  1244. */
  1245. public function __clone()
  1246. {
  1247. foreach ($this->sqlParts as $part => $elements) {
  1248. if (is_array($this->sqlParts[$part])) {
  1249. foreach ($this->sqlParts[$part] as $idx => $element) {
  1250. if (! is_object($element)) {
  1251. continue;
  1252. }
  1253. $this->sqlParts[$part][$idx] = clone $element;
  1254. }
  1255. } elseif (is_object($elements)) {
  1256. $this->sqlParts[$part] = clone $elements;
  1257. }
  1258. }
  1259. foreach ($this->params as $name => $param) {
  1260. if (! is_object($param)) {
  1261. continue;
  1262. }
  1263. $this->params[$name] = clone $param;
  1264. }
  1265. }
  1266. }