Expr.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the MIT license. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ORM\Query;
  20. use Traversable;
  21. use function func_get_args;
  22. use function implode;
  23. use function is_bool;
  24. use function is_iterable;
  25. use function is_numeric;
  26. use function is_string;
  27. use function iterator_to_array;
  28. use function str_replace;
  29. /**
  30. * This class is used to generate DQL expressions via a set of PHP static functions.
  31. *
  32. * @link www.doctrine-project.org
  33. *
  34. * @todo Rename: ExpressionBuilder
  35. */
  36. class Expr
  37. {
  38. /**
  39. * Creates a conjunction of the given boolean expressions.
  40. *
  41. * Example:
  42. *
  43. * [php]
  44. * // (u.type = ?1) AND (u.role = ?2)
  45. * $expr->andX($expr->eq('u.type', ':1'), $expr->eq('u.role', ':2'));
  46. *
  47. * @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string $x Optional clause. Defaults to null,
  48. * but requires at least one defined
  49. * when converting to string.
  50. *
  51. * @return Expr\Andx
  52. */
  53. public function andX($x = null)
  54. {
  55. return new Expr\Andx(func_get_args());
  56. }
  57. /**
  58. * Creates a disjunction of the given boolean expressions.
  59. *
  60. * Example:
  61. *
  62. * [php]
  63. * // (u.type = ?1) OR (u.role = ?2)
  64. * $q->where($q->expr()->orX('u.type = ?1', 'u.role = ?2'));
  65. *
  66. * @param Expr\Comparison|Expr\Func|Expr\Andx|Expr\Orx|string $x Optional clause. Defaults to null,
  67. * but requires at least one defined
  68. * when converting to string.
  69. *
  70. * @return Expr\Orx
  71. */
  72. public function orX($x = null)
  73. {
  74. return new Expr\Orx(func_get_args());
  75. }
  76. /**
  77. * Creates an ASCending order expression.
  78. *
  79. * @param mixed $expr
  80. *
  81. * @return Expr\OrderBy
  82. */
  83. public function asc($expr)
  84. {
  85. return new Expr\OrderBy($expr, 'ASC');
  86. }
  87. /**
  88. * Creates a DESCending order expression.
  89. *
  90. * @param mixed $expr
  91. *
  92. * @return Expr\OrderBy
  93. */
  94. public function desc($expr)
  95. {
  96. return new Expr\OrderBy($expr, 'DESC');
  97. }
  98. /**
  99. * Creates an equality comparison expression with the given arguments.
  100. *
  101. * First argument is considered the left expression and the second is the right expression.
  102. * When converted to string, it will generated a <left expr> = <right expr>. Example:
  103. *
  104. * [php]
  105. * // u.id = ?1
  106. * $expr->eq('u.id', '?1');
  107. *
  108. * @param mixed $x Left expression.
  109. * @param mixed $y Right expression.
  110. *
  111. * @return Expr\Comparison
  112. */
  113. public function eq($x, $y)
  114. {
  115. return new Expr\Comparison($x, Expr\Comparison::EQ, $y);
  116. }
  117. /**
  118. * Creates an instance of Expr\Comparison, with the given arguments.
  119. * First argument is considered the left expression and the second is the right expression.
  120. * When converted to string, it will generated a <left expr> <> <right expr>. Example:
  121. *
  122. * [php]
  123. * // u.id <> ?1
  124. * $q->where($q->expr()->neq('u.id', '?1'));
  125. *
  126. * @param mixed $x Left expression.
  127. * @param mixed $y Right expression.
  128. *
  129. * @return Expr\Comparison
  130. */
  131. public function neq($x, $y)
  132. {
  133. return new Expr\Comparison($x, Expr\Comparison::NEQ, $y);
  134. }
  135. /**
  136. * Creates an instance of Expr\Comparison, with the given arguments.
  137. * First argument is considered the left expression and the second is the right expression.
  138. * When converted to string, it will generated a <left expr> < <right expr>. Example:
  139. *
  140. * [php]
  141. * // u.id < ?1
  142. * $q->where($q->expr()->lt('u.id', '?1'));
  143. *
  144. * @param mixed $x Left expression.
  145. * @param mixed $y Right expression.
  146. *
  147. * @return Expr\Comparison
  148. */
  149. public function lt($x, $y)
  150. {
  151. return new Expr\Comparison($x, Expr\Comparison::LT, $y);
  152. }
  153. /**
  154. * Creates an instance of Expr\Comparison, with the given arguments.
  155. * First argument is considered the left expression and the second is the right expression.
  156. * When converted to string, it will generated a <left expr> <= <right expr>. Example:
  157. *
  158. * [php]
  159. * // u.id <= ?1
  160. * $q->where($q->expr()->lte('u.id', '?1'));
  161. *
  162. * @param mixed $x Left expression.
  163. * @param mixed $y Right expression.
  164. *
  165. * @return Expr\Comparison
  166. */
  167. public function lte($x, $y)
  168. {
  169. return new Expr\Comparison($x, Expr\Comparison::LTE, $y);
  170. }
  171. /**
  172. * Creates an instance of Expr\Comparison, with the given arguments.
  173. * First argument is considered the left expression and the second is the right expression.
  174. * When converted to string, it will generated a <left expr> > <right expr>. Example:
  175. *
  176. * [php]
  177. * // u.id > ?1
  178. * $q->where($q->expr()->gt('u.id', '?1'));
  179. *
  180. * @param mixed $x Left expression.
  181. * @param mixed $y Right expression.
  182. *
  183. * @return Expr\Comparison
  184. */
  185. public function gt($x, $y)
  186. {
  187. return new Expr\Comparison($x, Expr\Comparison::GT, $y);
  188. }
  189. /**
  190. * Creates an instance of Expr\Comparison, with the given arguments.
  191. * First argument is considered the left expression and the second is the right expression.
  192. * When converted to string, it will generated a <left expr> >= <right expr>. Example:
  193. *
  194. * [php]
  195. * // u.id >= ?1
  196. * $q->where($q->expr()->gte('u.id', '?1'));
  197. *
  198. * @param mixed $x Left expression.
  199. * @param mixed $y Right expression.
  200. *
  201. * @return Expr\Comparison
  202. */
  203. public function gte($x, $y)
  204. {
  205. return new Expr\Comparison($x, Expr\Comparison::GTE, $y);
  206. }
  207. /**
  208. * Creates an instance of AVG() function, with the given argument.
  209. *
  210. * @param mixed $x Argument to be used in AVG() function.
  211. *
  212. * @return Expr\Func
  213. */
  214. public function avg($x)
  215. {
  216. return new Expr\Func('AVG', [$x]);
  217. }
  218. /**
  219. * Creates an instance of MAX() function, with the given argument.
  220. *
  221. * @param mixed $x Argument to be used in MAX() function.
  222. *
  223. * @return Expr\Func
  224. */
  225. public function max($x)
  226. {
  227. return new Expr\Func('MAX', [$x]);
  228. }
  229. /**
  230. * Creates an instance of MIN() function, with the given argument.
  231. *
  232. * @param mixed $x Argument to be used in MIN() function.
  233. *
  234. * @return Expr\Func
  235. */
  236. public function min($x)
  237. {
  238. return new Expr\Func('MIN', [$x]);
  239. }
  240. /**
  241. * Creates an instance of COUNT() function, with the given argument.
  242. *
  243. * @param mixed $x Argument to be used in COUNT() function.
  244. *
  245. * @return Expr\Func
  246. */
  247. public function count($x)
  248. {
  249. return new Expr\Func('COUNT', [$x]);
  250. }
  251. /**
  252. * Creates an instance of COUNT(DISTINCT) function, with the given argument.
  253. *
  254. * @param mixed $x Argument to be used in COUNT(DISTINCT) function.
  255. *
  256. * @return string
  257. */
  258. public function countDistinct($x)
  259. {
  260. return 'COUNT(DISTINCT ' . implode(', ', func_get_args()) . ')';
  261. }
  262. /**
  263. * Creates an instance of EXISTS() function, with the given DQL Subquery.
  264. *
  265. * @param mixed $subquery DQL Subquery to be used in EXISTS() function.
  266. *
  267. * @return Expr\Func
  268. */
  269. public function exists($subquery)
  270. {
  271. return new Expr\Func('EXISTS', [$subquery]);
  272. }
  273. /**
  274. * Creates an instance of ALL() function, with the given DQL Subquery.
  275. *
  276. * @param mixed $subquery DQL Subquery to be used in ALL() function.
  277. *
  278. * @return Expr\Func
  279. */
  280. public function all($subquery)
  281. {
  282. return new Expr\Func('ALL', [$subquery]);
  283. }
  284. /**
  285. * Creates a SOME() function expression with the given DQL subquery.
  286. *
  287. * @param mixed $subquery DQL Subquery to be used in SOME() function.
  288. *
  289. * @return Expr\Func
  290. */
  291. public function some($subquery)
  292. {
  293. return new Expr\Func('SOME', [$subquery]);
  294. }
  295. /**
  296. * Creates an ANY() function expression with the given DQL subquery.
  297. *
  298. * @param mixed $subquery DQL Subquery to be used in ANY() function.
  299. *
  300. * @return Expr\Func
  301. */
  302. public function any($subquery)
  303. {
  304. return new Expr\Func('ANY', [$subquery]);
  305. }
  306. /**
  307. * Creates a negation expression of the given restriction.
  308. *
  309. * @param mixed $restriction Restriction to be used in NOT() function.
  310. *
  311. * @return Expr\Func
  312. */
  313. public function not($restriction)
  314. {
  315. return new Expr\Func('NOT', [$restriction]);
  316. }
  317. /**
  318. * Creates an ABS() function expression with the given argument.
  319. *
  320. * @param mixed $x Argument to be used in ABS() function.
  321. *
  322. * @return Expr\Func
  323. */
  324. public function abs($x)
  325. {
  326. return new Expr\Func('ABS', [$x]);
  327. }
  328. /**
  329. * Creates a product mathematical expression with the given arguments.
  330. *
  331. * First argument is considered the left expression and the second is the right expression.
  332. * When converted to string, it will generated a <left expr> * <right expr>. Example:
  333. *
  334. * [php]
  335. * // u.salary * u.percentAnnualSalaryIncrease
  336. * $q->expr()->prod('u.salary', 'u.percentAnnualSalaryIncrease')
  337. *
  338. * @param mixed $x Left expression.
  339. * @param mixed $y Right expression.
  340. *
  341. * @return Expr\Math
  342. */
  343. public function prod($x, $y)
  344. {
  345. return new Expr\Math($x, '*', $y);
  346. }
  347. /**
  348. * Creates a difference mathematical expression with the given arguments.
  349. * First argument is considered the left expression and the second is the right expression.
  350. * When converted to string, it will generated a <left expr> - <right expr>. Example:
  351. *
  352. * [php]
  353. * // u.monthlySubscriptionCount - 1
  354. * $q->expr()->diff('u.monthlySubscriptionCount', '1')
  355. *
  356. * @param mixed $x Left expression.
  357. * @param mixed $y Right expression.
  358. *
  359. * @return Expr\Math
  360. */
  361. public function diff($x, $y)
  362. {
  363. return new Expr\Math($x, '-', $y);
  364. }
  365. /**
  366. * Creates a sum mathematical expression with the given arguments.
  367. * First argument is considered the left expression and the second is the right expression.
  368. * When converted to string, it will generated a <left expr> + <right expr>. Example:
  369. *
  370. * [php]
  371. * // u.numChildren + 1
  372. * $q->expr()->sum('u.numChildren', '1')
  373. *
  374. * @param mixed $x Left expression.
  375. * @param mixed $y Right expression.
  376. *
  377. * @return Expr\Math
  378. */
  379. public function sum($x, $y)
  380. {
  381. return new Expr\Math($x, '+', $y);
  382. }
  383. /**
  384. * Creates a quotient mathematical expression with the given arguments.
  385. * First argument is considered the left expression and the second is the right expression.
  386. * When converted to string, it will generated a <left expr> / <right expr>. Example:
  387. *
  388. * [php]
  389. * // u.total / u.period
  390. * $expr->quot('u.total', 'u.period')
  391. *
  392. * @param mixed $x Left expression.
  393. * @param mixed $y Right expression.
  394. *
  395. * @return Expr\Math
  396. */
  397. public function quot($x, $y)
  398. {
  399. return new Expr\Math($x, '/', $y);
  400. }
  401. /**
  402. * Creates a SQRT() function expression with the given argument.
  403. *
  404. * @param mixed $x Argument to be used in SQRT() function.
  405. *
  406. * @return Expr\Func
  407. */
  408. public function sqrt($x)
  409. {
  410. return new Expr\Func('SQRT', [$x]);
  411. }
  412. /**
  413. * Creates an IN() expression with the given arguments.
  414. *
  415. * @param string $x Field in string format to be restricted by IN() function.
  416. * @param mixed $y Argument to be used in IN() function.
  417. *
  418. * @return Expr\Func
  419. */
  420. public function in($x, $y)
  421. {
  422. if (is_iterable($y)) {
  423. if ($y instanceof Traversable) {
  424. $y = iterator_to_array($y);
  425. }
  426. foreach ($y as &$literal) {
  427. if (! ($literal instanceof Expr\Literal)) {
  428. $literal = $this->quoteLiteral($literal);
  429. }
  430. }
  431. }
  432. return new Expr\Func($x . ' IN', (array) $y);
  433. }
  434. /**
  435. * Creates a NOT IN() expression with the given arguments.
  436. *
  437. * @param string $x Field in string format to be restricted by NOT IN() function.
  438. * @param mixed $y Argument to be used in NOT IN() function.
  439. *
  440. * @return Expr\Func
  441. */
  442. public function notIn($x, $y)
  443. {
  444. if (is_iterable($y)) {
  445. if ($y instanceof Traversable) {
  446. $y = iterator_to_array($y);
  447. }
  448. foreach ($y as &$literal) {
  449. if (! ($literal instanceof Expr\Literal)) {
  450. $literal = $this->quoteLiteral($literal);
  451. }
  452. }
  453. }
  454. return new Expr\Func($x . ' NOT IN', (array) $y);
  455. }
  456. /**
  457. * Creates an IS NULL expression with the given arguments.
  458. *
  459. * @param string $x Field in string format to be restricted by IS NULL.
  460. *
  461. * @return string
  462. */
  463. public function isNull($x)
  464. {
  465. return $x . ' IS NULL';
  466. }
  467. /**
  468. * Creates an IS NOT NULL expression with the given arguments.
  469. *
  470. * @param string $x Field in string format to be restricted by IS NOT NULL.
  471. *
  472. * @return string
  473. */
  474. public function isNotNull($x)
  475. {
  476. return $x . ' IS NOT NULL';
  477. }
  478. /**
  479. * Creates a LIKE() comparison expression with the given arguments.
  480. *
  481. * @param string $x Field in string format to be inspected by LIKE() comparison.
  482. * @param mixed $y Argument to be used in LIKE() comparison.
  483. *
  484. * @return Expr\Comparison
  485. */
  486. public function like($x, $y)
  487. {
  488. return new Expr\Comparison($x, 'LIKE', $y);
  489. }
  490. /**
  491. * Creates a NOT LIKE() comparison expression with the given arguments.
  492. *
  493. * @param string $x Field in string format to be inspected by LIKE() comparison.
  494. * @param mixed $y Argument to be used in LIKE() comparison.
  495. *
  496. * @return Expr\Comparison
  497. */
  498. public function notLike($x, $y)
  499. {
  500. return new Expr\Comparison($x, 'NOT LIKE', $y);
  501. }
  502. /**
  503. * Creates a CONCAT() function expression with the given arguments.
  504. *
  505. * @param mixed $x First argument to be used in CONCAT() function.
  506. * @param mixed $y,... Other arguments to be used in CONCAT() function.
  507. *
  508. * @return Expr\Func
  509. */
  510. public function concat($x, $y)
  511. {
  512. return new Expr\Func('CONCAT', func_get_args());
  513. }
  514. /**
  515. * Creates a SUBSTRING() function expression with the given arguments.
  516. *
  517. * @param mixed $x Argument to be used as string to be cropped by SUBSTRING() function.
  518. * @param int $from Initial offset to start cropping string. May accept negative values.
  519. * @param int|null $len Length of crop. May accept negative values.
  520. *
  521. * @return Expr\Func
  522. */
  523. public function substring($x, $from, $len = null)
  524. {
  525. $args = [$x, $from];
  526. if ($len !== null) {
  527. $args[] = $len;
  528. }
  529. return new Expr\Func('SUBSTRING', $args);
  530. }
  531. /**
  532. * Creates a LOWER() function expression with the given argument.
  533. *
  534. * @param mixed $x Argument to be used in LOWER() function.
  535. *
  536. * @return Expr\Func A LOWER function expression.
  537. */
  538. public function lower($x)
  539. {
  540. return new Expr\Func('LOWER', [$x]);
  541. }
  542. /**
  543. * Creates an UPPER() function expression with the given argument.
  544. *
  545. * @param mixed $x Argument to be used in UPPER() function.
  546. *
  547. * @return Expr\Func An UPPER function expression.
  548. */
  549. public function upper($x)
  550. {
  551. return new Expr\Func('UPPER', [$x]);
  552. }
  553. /**
  554. * Creates a LENGTH() function expression with the given argument.
  555. *
  556. * @param mixed $x Argument to be used as argument of LENGTH() function.
  557. *
  558. * @return Expr\Func A LENGTH function expression.
  559. */
  560. public function length($x)
  561. {
  562. return new Expr\Func('LENGTH', [$x]);
  563. }
  564. /**
  565. * Creates a literal expression of the given argument.
  566. *
  567. * @param mixed $literal Argument to be converted to literal.
  568. *
  569. * @return Expr\Literal
  570. */
  571. public function literal($literal)
  572. {
  573. return new Expr\Literal($this->quoteLiteral($literal));
  574. }
  575. /**
  576. * Quotes a literal value, if necessary, according to the DQL syntax.
  577. *
  578. * @param mixed $literal The literal value.
  579. */
  580. private function quoteLiteral($literal): string
  581. {
  582. if (is_numeric($literal) && ! is_string($literal)) {
  583. return (string) $literal;
  584. } elseif (is_bool($literal)) {
  585. return $literal ? 'true' : 'false';
  586. }
  587. return "'" . str_replace("'", "''", $literal) . "'";
  588. }
  589. /**
  590. * Creates an instance of BETWEEN() function, with the given argument.
  591. *
  592. * @param mixed $val Valued to be inspected by range values.
  593. * @param int|string $x Starting range value to be used in BETWEEN() function.
  594. * @param int|string $y End point value to be used in BETWEEN() function.
  595. *
  596. * @return string A BETWEEN expression.
  597. */
  598. public function between($val, $x, $y)
  599. {
  600. return $val . ' BETWEEN ' . $x . ' AND ' . $y;
  601. }
  602. /**
  603. * Creates an instance of TRIM() function, with the given argument.
  604. *
  605. * @param mixed $x Argument to be used as argument of TRIM() function.
  606. *
  607. * @return Expr\Func a TRIM expression.
  608. */
  609. public function trim($x)
  610. {
  611. return new Expr\Func('TRIM', $x);
  612. }
  613. /**
  614. * Creates an instance of MEMBER OF function, with the given arguments.
  615. *
  616. * @param string $x Value to be checked
  617. * @param string $y Value to be checked against
  618. *
  619. * @return Expr\Comparison
  620. */
  621. public function isMemberOf($x, $y)
  622. {
  623. return new Expr\Comparison($x, 'MEMBER OF', $y);
  624. }
  625. /**
  626. * Creates an instance of INSTANCE OF function, with the given arguments.
  627. *
  628. * @param string $x Value to be checked
  629. * @param string $y Value to be checked against
  630. *
  631. * @return Expr\Comparison
  632. */
  633. public function isInstanceOf($x, $y)
  634. {
  635. return new Expr\Comparison($x, 'INSTANCE OF', $y);
  636. }
  637. }