Query.php 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\ORM;
  16. use ArrayObject;
  17. use Cake\Database\ExpressionInterface;
  18. use Cake\Database\Query as DatabaseQuery;
  19. use Cake\Database\ValueBinder;
  20. use Cake\Datasource\QueryInterface;
  21. use Cake\Datasource\QueryTrait;
  22. use JsonSerializable;
  23. use RuntimeException;
  24. /**
  25. * Extends the base Query class to provide new methods related to association
  26. * loading, automatic fields selection, automatic type casting and to wrap results
  27. * into a specific iterator that will be responsible for hydrating results if
  28. * required.
  29. *
  30. */
  31. class Query extends DatabaseQuery implements JsonSerializable, QueryInterface
  32. {
  33. use QueryTrait {
  34. cache as private _cache;
  35. all as private _all;
  36. _decorateResults as private _applyDecorators;
  37. __call as private _call;
  38. }
  39. /**
  40. * Indicates that the operation should append to the list
  41. *
  42. * @var int
  43. */
  44. const APPEND = 0;
  45. /**
  46. * Indicates that the operation should prepend to the list
  47. *
  48. * @var int
  49. */
  50. const PREPEND = 1;
  51. /**
  52. * Indicates that the operation should overwrite the list
  53. *
  54. * @var bool
  55. */
  56. const OVERWRITE = true;
  57. /**
  58. * Whether the user select any fields before being executed, this is used
  59. * to determined if any fields should be automatically be selected.
  60. *
  61. * @var bool
  62. */
  63. protected $_hasFields;
  64. /**
  65. * Tracks whether or not the original query should include
  66. * fields from the top level table.
  67. *
  68. * @var bool
  69. */
  70. protected $_autoFields;
  71. /**
  72. * Whether to hydrate results into entity objects
  73. *
  74. * @var bool
  75. */
  76. protected $_hydrate = true;
  77. /**
  78. * A callable function that can be used to calculate the total amount of
  79. * records this query will match when not using `limit`
  80. *
  81. * @var callable
  82. */
  83. protected $_counter;
  84. /**
  85. * Instance of a class responsible for storing association containments and
  86. * for eager loading them when this query is executed
  87. *
  88. * @var \Cake\ORM\EagerLoader
  89. */
  90. protected $_eagerLoader;
  91. /**
  92. * True if the beforeFind event has already been triggered for this query
  93. *
  94. * @var bool
  95. */
  96. protected $_beforeFindFired = false;
  97. /**
  98. * Constructor
  99. *
  100. * @param \Cake\Database\Connection $connection The connection object
  101. * @param \Cake\ORM\Table $table The table this query is starting on
  102. */
  103. public function __construct($connection, $table)
  104. {
  105. parent::__construct($connection);
  106. $this->repository($table);
  107. if ($this->_repository) {
  108. $this->addDefaultTypes($this->_repository);
  109. }
  110. }
  111. /**
  112. * {@inheritDoc}
  113. *
  114. * If you pass an instance of a `Cake\ORM\Table` or `Cake\ORM\Association` class,
  115. * all the fields in the schema of the table or the association will be added to
  116. * the select clause.
  117. *
  118. * @param array|ExpressionInterface|string|\Cake\ORM\Table|\Cake\ORM\Association $fields fields
  119. * to be added to the list.
  120. * @param bool $overwrite whether to reset fields with passed list or not
  121. */
  122. public function select($fields = [], $overwrite = false)
  123. {
  124. if ($fields instanceof Association) {
  125. $fields = $fields->target();
  126. }
  127. if ($fields instanceof Table) {
  128. $fields = $this->aliasFields($fields->schema()->columns(), $fields->alias());
  129. }
  130. return parent::select($fields, $overwrite);
  131. }
  132. /**
  133. * Hints this object to associate the correct types when casting conditions
  134. * for the database. This is done by extracting the field types from the schema
  135. * associated to the passed table object. This prevents the user from repeating
  136. * himself when specifying conditions.
  137. *
  138. * This method returns the same query object for chaining.
  139. *
  140. * @param \Cake\ORM\Table $table The table to pull types from
  141. * @return $this
  142. */
  143. public function addDefaultTypes(Table $table)
  144. {
  145. $alias = $table->alias();
  146. $schema = $table->schema();
  147. $fields = [];
  148. foreach ($schema->columns() as $f) {
  149. $fields[$f] = $fields[$alias . '.' . $f] = $schema->columnType($f);
  150. }
  151. $this->typeMap()->addDefaults($fields);
  152. return $this;
  153. }
  154. /**
  155. * Sets the instance of the eager loader class to use for loading associations
  156. * and storing containments. If called with no arguments, it will return the
  157. * currently configured instance.
  158. *
  159. * @param \Cake\ORM\EagerLoader $instance The eager loader to use. Pass null
  160. * to get the current eagerloader.
  161. * @return \Cake\ORM\EagerLoader|$this
  162. */
  163. public function eagerLoader(EagerLoader $instance = null)
  164. {
  165. if ($instance === null) {
  166. if ($this->_eagerLoader === null) {
  167. $this->_eagerLoader = new EagerLoader;
  168. }
  169. return $this->_eagerLoader;
  170. }
  171. $this->_eagerLoader = $instance;
  172. return $this;
  173. }
  174. /**
  175. * Sets the list of associations that should be eagerly loaded along with this
  176. * query. The list of associated tables passed must have been previously set as
  177. * associations using the Table API.
  178. *
  179. * ### Example:
  180. *
  181. * ```
  182. * // Bring articles' author information
  183. * $query->contain('Author');
  184. *
  185. * // Also bring the category and tags associated to each article
  186. * $query->contain(['Category', 'Tag']);
  187. * ```
  188. *
  189. * Associations can be arbitrarily nested using dot notation or nested arrays,
  190. * this allows this object to calculate joins or any additional queries that
  191. * must be executed to bring the required associated data.
  192. *
  193. * ### Example:
  194. *
  195. * ```
  196. * // Eager load the product info, and for each product load other 2 associations
  197. * $query->contain(['Product' => ['Manufacturer', 'Distributor']);
  198. *
  199. * // Which is equivalent to calling
  200. * $query->contain(['Products.Manufactures', 'Products.Distributors']);
  201. *
  202. * // For an author query, load his region, state and country
  203. * $query->contain('Regions.States.Countries');
  204. * ```
  205. *
  206. * It is possible to control the conditions and fields selected for each of the
  207. * contained associations:
  208. *
  209. * ### Example:
  210. *
  211. * ```
  212. * $query->contain(['Tags' => function ($q) {
  213. * return $q->where(['Tags.is_popular' => true]);
  214. * }]);
  215. *
  216. * $query->contain(['Products.Manufactures' => function ($q) {
  217. * return $q->select(['name'])->where(['Manufactures.active' => true]);
  218. * }]);
  219. * ```
  220. *
  221. * Each association might define special options when eager loaded, the allowed
  222. * options that can be set per association are:
  223. *
  224. * - foreignKey: Used to set a different field to match both tables, if set to false
  225. * no join conditions will be generated automatically. `false` can only be used on
  226. * joinable associations and cannot be used with hasMany or belongsToMany associations.
  227. * - fields: An array with the fields that should be fetched from the association
  228. * - queryBuilder: Equivalent to passing a callable instead of an options array
  229. *
  230. * ### Example:
  231. *
  232. * ```
  233. * // Set options for the hasMany articles that will be eagerly loaded for an author
  234. * $query->contain([
  235. * 'Articles' => [
  236. * 'fields' => ['title', 'author_id']
  237. * ]
  238. * ]);
  239. * ```
  240. *
  241. * When containing associations, it is important to include foreign key columns.
  242. * Failing to do so will trigger exceptions.
  243. *
  244. * ```
  245. * // Use special join conditions for getting an Articles's belongsTo 'authors'
  246. * $query->contain([
  247. * 'Authors' => [
  248. * 'foreignKey' => false,
  249. * 'queryBuilder' => function ($q) {
  250. * return $q->where(...); // Add full filtering conditions
  251. * }
  252. * ]
  253. * ]);
  254. * ```
  255. *
  256. * If called with no arguments, this function will return an array with
  257. * with the list of previously configured associations to be contained in the
  258. * result.
  259. *
  260. * If called with an empty first argument and $override is set to true, the
  261. * previous list will be emptied.
  262. *
  263. * @param array|string $associations list of table aliases to be queried
  264. * @param bool $override whether override previous list with the one passed
  265. * defaults to merging previous list with the new one.
  266. * @return array|$this
  267. */
  268. public function contain($associations = null, $override = false)
  269. {
  270. $loader = $this->eagerLoader();
  271. if ($override === true) {
  272. $loader->clearContain();
  273. $this->_dirty();
  274. }
  275. $result = $loader->contain($associations);
  276. if ($associations === null) {
  277. return $result;
  278. }
  279. $this->_addAssociationsToTypeMap($this->repository(), $this->typeMap(), $result);
  280. return $this;
  281. }
  282. /**
  283. * Used to recursively add contained association column types to
  284. * the query.
  285. *
  286. * @param \Cake\ORM\Table $table The table instance to pluck associations from.
  287. * @param \Cake\Database\TypeMap $typeMap The typemap to check for columns in.
  288. * This typemap is indirectly mutated via Cake\ORM\Query::addDefaultTypes()
  289. * @param array $associations The nested tree of associations to walk.
  290. * @return void
  291. */
  292. protected function _addAssociationsToTypeMap($table, $typeMap, $associations)
  293. {
  294. $typeMap = $this->typeMap();
  295. foreach ($associations as $name => $nested) {
  296. $association = $table->association($name);
  297. if (!$association) {
  298. continue;
  299. }
  300. $target = $association->target();
  301. $primary = (array)$target->primaryKey();
  302. if ($typeMap->type($target->aliasField($primary[0])) === null) {
  303. $this->addDefaultTypes($target);
  304. }
  305. if (!empty($nested)) {
  306. $this->_addAssociationsToTypeMap($target, $typeMap, $nested);
  307. }
  308. }
  309. }
  310. /**
  311. * Adds filtering conditions to this query to only bring rows that have a relation
  312. * to another from an associated table, based on conditions in the associated table.
  313. *
  314. * This function will add entries in the `contain` graph.
  315. *
  316. * ### Example:
  317. *
  318. * ```
  319. * // Bring only articles that were tagged with 'cake'
  320. * $query->matching('Tags', function ($q) {
  321. * return $q->where(['name' => 'cake']);
  322. * );
  323. * ```
  324. *
  325. * It is possible to filter by deep associations by using dot notation:
  326. *
  327. * ### Example:
  328. *
  329. * ```
  330. * // Bring only articles that were commented by 'markstory'
  331. * $query->matching('Comments.Users', function ($q) {
  332. * return $q->where(['username' => 'markstory']);
  333. * );
  334. * ```
  335. *
  336. * As this function will create `INNER JOIN`, you might want to consider
  337. * calling `distinct` on this query as you might get duplicate rows if
  338. * your conditions don't filter them already. This might be the case, for example,
  339. * of the same user commenting more than once in the same article.
  340. *
  341. * ### Example:
  342. *
  343. * ```
  344. * // Bring unique articles that were commented by 'markstory'
  345. * $query->distinct(['Articles.id'])
  346. * ->matching('Comments.Users', function ($q) {
  347. * return $q->where(['username' => 'markstory']);
  348. * );
  349. * ```
  350. *
  351. * Please note that the query passed to the closure will only accept calling
  352. * `select`, `where`, `andWhere` and `orWhere` on it. If you wish to
  353. * add more complex clauses you can do it directly in the main query.
  354. *
  355. * @param string $assoc The association to filter by
  356. * @param callable $builder a function that will receive a pre-made query object
  357. * that can be used to add custom conditions or selecting some fields
  358. * @return $this
  359. */
  360. public function matching($assoc, callable $builder = null)
  361. {
  362. $this->eagerLoader()->matching($assoc, $builder);
  363. $this->_dirty();
  364. return $this;
  365. }
  366. /**
  367. * Creates a LEFT JOIN with the passed association table while preserving
  368. * the foreign key matching and the custom conditions that were originally set
  369. * for it.
  370. *
  371. * This function will add entries in the `contain` graph.
  372. *
  373. * ### Example:
  374. *
  375. * ```
  376. * // Get the count of articles per user
  377. * $usersQuery
  378. * ->select(['total_articles' => $query->func()->count('Articles.id')])
  379. * ->leftJoinWith('Articles')
  380. * ->group(['Users.id'])
  381. * ->autoFields(true);
  382. * ```
  383. *
  384. * You can also customize the conditions passed to the LEFT JOIN:
  385. *
  386. * ```
  387. * // Get the count of articles per user with at least 5 votes
  388. * $usersQuery
  389. * ->select(['total_articles' => $query->func()->count('Articles.id')])
  390. * ->leftJoinWith('Articles', function ($q) {
  391. * return $q->where(['Articles.votes >=' => 5]);
  392. * })
  393. * ->group(['Users.id'])
  394. * ->autoFields(true);
  395. * ```
  396. *
  397. * This will create the following SQL:
  398. *
  399. * ```
  400. * SELECT COUNT(Articles.id) AS total_articles, Users.*
  401. * FROM users Users
  402. * LEFT JOIN articles Articles ON Articles.user_id = Users.id AND Articles.votes >= 5
  403. * GROUP BY USers.id
  404. * ```
  405. *
  406. * It is possible to left join deep associations by using dot notation
  407. *
  408. * ### Example:
  409. *
  410. * ```
  411. * // Total comments in articles by 'markstory'
  412. * $query
  413. * ->select(['total_comments' => $query->func()->count('Comments.id')])
  414. * ->leftJoinWith('Comments.Users', function ($q) {
  415. * return $q->where(['username' => 'markstory']);
  416. * )
  417. * ->group(['Users.id']);
  418. * ```
  419. *
  420. * Please note that the query passed to the closure will only accept calling
  421. * `select`, `where`, `andWhere` and `orWhere` on it. If you wish to
  422. * add more complex clauses you can do it directly in the main query.
  423. *
  424. * @param string $assoc The association to join with
  425. * @param callable $builder a function that will receive a pre-made query object
  426. * that can be used to add custom conditions or selecting some fields
  427. * @return $this
  428. */
  429. public function leftJoinWith($assoc, callable $builder = null)
  430. {
  431. $this->eagerLoader()->matching($assoc, $builder, [
  432. 'joinType' => 'LEFT',
  433. 'fields' => false
  434. ]);
  435. $this->_dirty();
  436. return $this;
  437. }
  438. /**
  439. * Creates an INNER JOIN with the passed association table while preserving
  440. * the foreign key matching and the custom conditions that were originally set
  441. * for it.
  442. *
  443. * This function will add entries in the `contain` graph.
  444. *
  445. * ### Example:
  446. *
  447. * ```
  448. * // Bring only articles that were tagged with 'cake'
  449. * $query->innerJoinWith('Tags', function ($q) {
  450. * return $q->where(['name' => 'cake']);
  451. * );
  452. * ```
  453. *
  454. * This will create the following SQL:
  455. *
  456. * ```
  457. * SELECT Articles.*
  458. * FROM articles Articles
  459. * INNER JOIN tags Tags ON Tags.name = 'cake'
  460. * INNER JOIN articles_tags ArticlesTags ON ArticlesTags.tag_id = Tags.id
  461. * AND ArticlesTags.articles_id = Articles.id
  462. * ```
  463. *
  464. * This function works the same as `matching()` with the difference that it
  465. * will select no fields from the association.
  466. *
  467. * @param string $assoc The association to join with
  468. * @param callable $builder a function that will receive a pre-made query object
  469. * that can be used to add custom conditions or selecting some fields
  470. * @return $this
  471. * @see \Cake\ORM\Query::matching()
  472. */
  473. public function innerJoinWith($assoc, callable $builder = null)
  474. {
  475. $this->eagerLoader()->matching($assoc, $builder, [
  476. 'joinType' => 'INNER',
  477. 'fields' => false
  478. ]);
  479. $this->_dirty();
  480. return $this;
  481. }
  482. /**
  483. * Adds filtering conditions to this query to only bring rows that have no match
  484. * to another from an associated table, based on conditions in the associated table.
  485. *
  486. * This function will add entries in the `contain` graph.
  487. *
  488. * ### Example:
  489. *
  490. * ```
  491. * // Bring only articles that were not tagged with 'cake'
  492. * $query->notMatching('Tags', function ($q) {
  493. * return $q->where(['name' => 'cake']);
  494. * );
  495. * ```
  496. *
  497. * It is possible to filter by deep associations by using dot notation:
  498. *
  499. * ### Example:
  500. *
  501. * ```
  502. * // Bring only articles that weren't commented by 'markstory'
  503. * $query->notMatching('Comments.Users', function ($q) {
  504. * return $q->where(['username' => 'markstory']);
  505. * );
  506. * ```
  507. *
  508. * As this function will create a `LEFT JOIN`, you might want to consider
  509. * calling `distinct` on this query as you might get duplicate rows if
  510. * your conditions don't filter them already. This might be the case, for example,
  511. * of the same article having multiple comments.
  512. *
  513. * ### Example:
  514. *
  515. * ```
  516. * // Bring unique articles that were commented by 'markstory'
  517. * $query->distinct(['Articles.id'])
  518. * ->notMatching('Comments.Users', function ($q) {
  519. * return $q->where(['username' => 'markstory']);
  520. * );
  521. * ```
  522. *
  523. * Please note that the query passed to the closure will only accept calling
  524. * `select`, `where`, `andWhere` and `orWhere` on it. If you wish to
  525. * add more complex clauses you can do it directly in the main query.
  526. *
  527. * @param string $assoc The association to filter by
  528. * @param callable $builder a function that will receive a pre-made query object
  529. * that can be used to add custom conditions or selecting some fields
  530. * @return $this
  531. */
  532. public function notMatching($assoc, callable $builder = null)
  533. {
  534. $this->eagerLoader()->matching($assoc, $builder, [
  535. 'joinType' => 'LEFT',
  536. 'fields' => false,
  537. 'negateMatch' => true
  538. ]);
  539. $this->_dirty();
  540. return $this;
  541. }
  542. /**
  543. * {@inheritDoc}
  544. *
  545. * Populates or adds parts to current query clauses using an array.
  546. * This is handy for passing all query clauses at once. The option array accepts:
  547. *
  548. * - fields: Maps to the select method
  549. * - conditions: Maps to the where method
  550. * - limit: Maps to the limit method
  551. * - order: Maps to the order method
  552. * - offset: Maps to the offset method
  553. * - group: Maps to the group method
  554. * - having: Maps to the having method
  555. * - contain: Maps to the contain options for eager loading
  556. * - join: Maps to the join method
  557. * - page: Maps to the page method
  558. *
  559. * ### Example:
  560. *
  561. * ```
  562. * $query->applyOptions([
  563. * 'fields' => ['id', 'name'],
  564. * 'conditions' => [
  565. * 'created >=' => '2013-01-01'
  566. * ],
  567. * 'limit' => 10
  568. * ]);
  569. * ```
  570. *
  571. * Is equivalent to:
  572. *
  573. * ```
  574. * $query
  575. * ->select(['id', 'name'])
  576. * ->where(['created >=' => '2013-01-01'])
  577. * ->limit(10)
  578. * ```
  579. */
  580. public function applyOptions(array $options)
  581. {
  582. $valid = [
  583. 'fields' => 'select',
  584. 'conditions' => 'where',
  585. 'join' => 'join',
  586. 'order' => 'order',
  587. 'limit' => 'limit',
  588. 'offset' => 'offset',
  589. 'group' => 'group',
  590. 'having' => 'having',
  591. 'contain' => 'contain',
  592. 'page' => 'page',
  593. ];
  594. ksort($options);
  595. foreach ($options as $option => $values) {
  596. if (isset($valid[$option]) && isset($values)) {
  597. $this->{$valid[$option]}($values);
  598. } else {
  599. $this->_options[$option] = $values;
  600. }
  601. }
  602. return $this;
  603. }
  604. /**
  605. * Creates a copy of this current query, triggers beforeFind and resets some state.
  606. *
  607. * The following state will be cleared:
  608. *
  609. * - autoFields
  610. * - limit
  611. * - offset
  612. * - map/reduce functions
  613. * - result formatters
  614. * - order
  615. * - containments
  616. *
  617. * This method creates query clones that are useful when working with subqueries.
  618. *
  619. * @return \Cake\ORM\Query
  620. */
  621. public function cleanCopy()
  622. {
  623. $clone = clone $this;
  624. $clone->_iterator = null;
  625. $clone->triggerBeforeFind();
  626. $clone->eagerLoader(clone $this->eagerLoader());
  627. $clone->valueBinder(clone $this->valueBinder());
  628. $clone->autoFields(false);
  629. $clone->limit(null);
  630. $clone->order([], true);
  631. $clone->offset(null);
  632. $clone->mapReduce(null, null, true);
  633. $clone->formatResults(null, true);
  634. return $clone;
  635. }
  636. /**
  637. * Object clone hook.
  638. *
  639. * Destroys the clones inner iterator and clones the value binder, and eagerloader instances.
  640. *
  641. * @return void
  642. */
  643. public function __clone()
  644. {
  645. parent::__clone();
  646. if ($this->_eagerLoader) {
  647. $this->_eagerLoader = clone $this->_eagerLoader;
  648. }
  649. }
  650. /**
  651. * {@inheritDoc}
  652. *
  653. * Returns the COUNT(*) for the query.
  654. */
  655. public function count()
  656. {
  657. $query = $this->cleanCopy();
  658. $counter = $this->_counter;
  659. if ($counter) {
  660. $query->counter(null);
  661. return (int)$counter($query);
  662. }
  663. $complex = (
  664. $query->clause('distinct') ||
  665. count($query->clause('group')) ||
  666. count($query->clause('union')) ||
  667. $query->clause('having')
  668. );
  669. if (!$complex) {
  670. // Expression fields could have bound parameters.
  671. foreach ($query->clause('select') as $field) {
  672. if ($field instanceof ExpressionInterface) {
  673. $complex = true;
  674. break;
  675. }
  676. }
  677. }
  678. $count = ['count' => $query->func()->count('*')];
  679. if (!$complex) {
  680. $query->eagerLoader()->autoFields(false);
  681. $statement = $query
  682. ->select($count, true)
  683. ->autoFields(false)
  684. ->execute();
  685. } else {
  686. $statement = $this->connection()->newQuery()
  687. ->select($count)
  688. ->from(['count_source' => $query])
  689. ->execute();
  690. }
  691. $result = $statement->fetch('assoc')['count'];
  692. $statement->closeCursor();
  693. return (int)$result;
  694. }
  695. /**
  696. * Registers a callable function that will be executed when the `count` method in
  697. * this query is called. The return value for the function will be set as the
  698. * return value of the `count` method.
  699. *
  700. * This is particularly useful when you need to optimize a query for returning the
  701. * count, for example removing unnecessary joins, removing group by or just return
  702. * an estimated number of rows.
  703. *
  704. * The callback will receive as first argument a clone of this query and not this
  705. * query itself.
  706. *
  707. * @param callable $counter The counter value
  708. * @return $this
  709. */
  710. public function counter($counter)
  711. {
  712. $this->_counter = $counter;
  713. return $this;
  714. }
  715. /**
  716. * Toggle hydrating entities.
  717. *
  718. * If set to false array results will be returned
  719. *
  720. * @param bool|null $enable Use a boolean to set the hydration mode.
  721. * Null will fetch the current hydration mode.
  722. * @return bool|$this A boolean when reading, and $this when setting the mode.
  723. */
  724. public function hydrate($enable = null)
  725. {
  726. if ($enable === null) {
  727. return $this->_hydrate;
  728. }
  729. $this->_dirty();
  730. $this->_hydrate = (bool)$enable;
  731. return $this;
  732. }
  733. /**
  734. * {@inheritDoc}
  735. *
  736. * @return $this
  737. * @throws \RuntimeException When you attempt to cache a non-select query.
  738. */
  739. public function cache($key, $config = 'default')
  740. {
  741. if ($this->_type !== 'select' && $this->_type !== null) {
  742. throw new RuntimeException('You cannot cache the results of non-select queries.');
  743. }
  744. return $this->_cache($key, $config);
  745. }
  746. /**
  747. * {@inheritDoc}
  748. *
  749. * @throws \RuntimeException if this method is called on a non-select Query.
  750. */
  751. public function all()
  752. {
  753. if ($this->_type !== 'select' && $this->_type !== null) {
  754. throw new RuntimeException(
  755. 'You cannot call all() on a non-select query. Use execute() instead.'
  756. );
  757. }
  758. return $this->_all();
  759. }
  760. /**
  761. * Trigger the beforeFind event on the query's repository object.
  762. *
  763. * Will not trigger more than once, and only for select queries.
  764. *
  765. * @return void
  766. */
  767. public function triggerBeforeFind()
  768. {
  769. if (!$this->_beforeFindFired && $this->_type === 'select') {
  770. $table = $this->repository();
  771. $table->dispatchEvent('Model.beforeFind', [
  772. $this,
  773. new ArrayObject($this->_options),
  774. !$this->eagerLoaded()
  775. ]);
  776. $this->_beforeFindFired = true;
  777. }
  778. }
  779. /**
  780. * {@inheritDoc}
  781. */
  782. public function sql(ValueBinder $binder = null)
  783. {
  784. $this->triggerBeforeFind();
  785. $this->_transformQuery();
  786. $sql = parent::sql($binder);
  787. return $sql;
  788. }
  789. /**
  790. * Executes this query and returns a ResultSet object containing the results.
  791. * This will also setup the correct statement class in order to eager load deep
  792. * associations.
  793. *
  794. * @return \Cake\ORM\ResultSet
  795. */
  796. protected function _execute()
  797. {
  798. $this->triggerBeforeFind();
  799. if ($this->_results) {
  800. $decorator = $this->_decoratorClass();
  801. return new $decorator($this->_results);
  802. }
  803. $statement = $this->eagerLoader()->loadExternal($this, $this->execute());
  804. return new ResultSet($this, $statement);
  805. }
  806. /**
  807. * Applies some defaults to the query object before it is executed.
  808. *
  809. * Specifically add the FROM clause, adds default table fields if none are
  810. * specified and applies the joins required to eager load associations defined
  811. * using `contain`
  812. *
  813. * @see \Cake\Database\Query::execute()
  814. * @return void
  815. */
  816. protected function _transformQuery()
  817. {
  818. if (!$this->_dirty) {
  819. return;
  820. }
  821. if ($this->_type === 'select') {
  822. if (empty($this->_parts['from'])) {
  823. $this->from([$this->_repository->alias() => $this->_repository->table()]);
  824. }
  825. $this->_addDefaultFields();
  826. $this->eagerLoader()->attachAssociations($this, $this->_repository, !$this->_hasFields);
  827. }
  828. }
  829. /**
  830. * Inspects if there are any set fields for selecting, otherwise adds all
  831. * the fields for the default table.
  832. *
  833. * @return void
  834. */
  835. protected function _addDefaultFields()
  836. {
  837. $select = $this->clause('select');
  838. $this->_hasFields = true;
  839. if (!count($select) || $this->_autoFields === true) {
  840. $this->_hasFields = false;
  841. $this->select($this->repository()->schema()->columns());
  842. $select = $this->clause('select');
  843. }
  844. $aliased = $this->aliasFields($select, $this->repository()->alias());
  845. $this->select($aliased, true);
  846. }
  847. /**
  848. * {@inheritDoc}
  849. *
  850. * @see \Cake\ORM\Table::find()
  851. */
  852. public function find($finder, array $options = [])
  853. {
  854. return $this->repository()->callFinder($finder, $this, $options);
  855. }
  856. /**
  857. * Marks a query as dirty, removing any preprocessed information
  858. * from in memory caching such as previous results
  859. *
  860. * @return void
  861. */
  862. protected function _dirty()
  863. {
  864. $this->_results = null;
  865. parent::_dirty();
  866. }
  867. /**
  868. * Create an update query.
  869. *
  870. * This changes the query type to be 'update'.
  871. * Can be combined with set() and where() methods to create update queries.
  872. *
  873. * @param string|null $table Unused parameter.
  874. * @return $this
  875. */
  876. public function update($table = null)
  877. {
  878. $table = $table ?: $this->repository()->table();
  879. return parent::update($table);
  880. }
  881. /**
  882. * Create a delete query.
  883. *
  884. * This changes the query type to be 'delete'.
  885. * Can be combined with the where() method to create delete queries.
  886. *
  887. * @param string|null $table Unused parameter.
  888. * @return $this
  889. */
  890. public function delete($table = null)
  891. {
  892. $repo = $this->repository();
  893. $this->from([$repo->alias() => $repo->table()]);
  894. return parent::delete();
  895. }
  896. /**
  897. * Create an insert query.
  898. *
  899. * This changes the query type to be 'insert'.
  900. * Note calling this method will reset any data previously set
  901. * with Query::values()
  902. *
  903. * Can be combined with the where() method to create delete queries.
  904. *
  905. * @param array $columns The columns to insert into.
  906. * @param array $types A map between columns & their datatypes.
  907. * @return $this
  908. */
  909. public function insert(array $columns, array $types = [])
  910. {
  911. $table = $this->repository()->table();
  912. $this->into($table);
  913. return parent::insert($columns, $types);
  914. }
  915. /**
  916. * {@inheritDoc}
  917. *
  918. * @throws \BadMethodCallException if the method is called for a non-select query
  919. */
  920. public function __call($method, $arguments)
  921. {
  922. if ($this->type() === 'select') {
  923. return $this->_call($method, $arguments);
  924. }
  925. throw new \BadMethodCallException(
  926. sprintf('Cannot call method "%s" on a "%s" query', $method, $this->type())
  927. );
  928. }
  929. /**
  930. * {@inheritDoc}
  931. */
  932. public function __debugInfo()
  933. {
  934. $eagerLoader = $this->eagerLoader();
  935. return parent::__debugInfo() + [
  936. 'hydrate' => $this->_hydrate,
  937. 'buffered' => $this->_useBufferedResults,
  938. 'formatters' => count($this->_formatters),
  939. 'mapReducers' => count($this->_mapReduce),
  940. 'contain' => $eagerLoader ? $eagerLoader->contain() : [],
  941. 'matching' => $eagerLoader ? $eagerLoader->matching() : [],
  942. 'extraOptions' => $this->_options,
  943. 'repository' => $this->_repository
  944. ];
  945. }
  946. /**
  947. * Executes the query and converts the result set into JSON.
  948. *
  949. * Part of JsonSerializable interface.
  950. *
  951. * @return \Cake\Datasource\ResultSetInterface The data to convert to JSON.
  952. */
  953. public function jsonSerialize()
  954. {
  955. return $this->all();
  956. }
  957. /**
  958. * Get/Set whether or not the ORM should automatically append fields.
  959. *
  960. * By default calling select() will disable auto-fields. You can re-enable
  961. * auto-fields with this method.
  962. *
  963. * @param bool|null $value The value to set or null to read the current value.
  964. * @return bool|$this Either the current value or the query object.
  965. */
  966. public function autoFields($value = null)
  967. {
  968. if ($value === null) {
  969. return $this->_autoFields;
  970. }
  971. $this->_autoFields = (bool)$value;
  972. return $this;
  973. }
  974. /**
  975. * Decorates the results iterator with MapReduce routines and formatters
  976. *
  977. * @param \Traversable $result Original results
  978. * @return \Cake\Datasource\ResultSetInterface
  979. */
  980. protected function _decorateResults($result)
  981. {
  982. $result = $this->_applyDecorators($result);
  983. if (!($result instanceof ResultSet) && $this->bufferResults()) {
  984. $class = $this->_decoratorClass();
  985. $result = new $class($result->buffered());
  986. }
  987. return $result;
  988. }
  989. }