Query.php 34 KB

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