QueryBuilder.php 42 KB

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