/lib/Doctrine/ORM/Query/Expr.php

https://github.com/jaikdean/doctrine2 · PHP · 661 lines · 244 code · 46 blank · 371 comment · 7 complexity · 7df9928efc10be1ce660de93e0a71b91 MD5 · raw file

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