Query.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047
  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 CakePHP(tm) v 3.0.0
  13. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  14. */
  15. namespace Cake\ORM;
  16. use Cake\Collection\Iterator\MapReduce;
  17. use Cake\Database\Query as DatabaseQuery;
  18. use Cake\Event\Event;
  19. use Cake\ORM\EagerLoader;
  20. use Cake\ORM\QueryCacher;
  21. use Cake\ORM\Table;
  22. /**
  23. * Extends the base Query class to provide new methods related to association
  24. * loading, automatic fields selection, automatic type casting and to wrap results
  25. * into an specific iterator that will be responsible for hydrating results if
  26. * required.
  27. *
  28. */
  29. class Query extends DatabaseQuery {
  30. /**
  31. * Indicates that the operation should append to the list
  32. *
  33. * @var integer
  34. */
  35. const APPEND = 0;
  36. /**
  37. * Indicates that the operation should prepend to the list
  38. *
  39. * @var integer
  40. */
  41. const PREPEND = 1;
  42. /**
  43. * Indicates that the operation should overwrite the list
  44. *
  45. * @var boolean
  46. */
  47. const OVERWRITE = true;
  48. /**
  49. * Instance of a table object this query is bound to
  50. *
  51. * @var \Cake\ORM\Table
  52. */
  53. protected $_table;
  54. /**
  55. * Whether the user select any fields before being executed, this is used
  56. * to determined if any fields should be automatically be selected.
  57. *
  58. * @var boolean
  59. */
  60. protected $_hasFields;
  61. /**
  62. * A ResultSet.
  63. *
  64. * When set, query execution will be bypassed.
  65. *
  66. * @var Cake\ORM\ResultSet
  67. * @see setResult()
  68. */
  69. protected $_results;
  70. /**
  71. * Boolean for tracking whether or not buffered results
  72. * are enabled.
  73. *
  74. * @var boolean
  75. */
  76. protected $_useBufferedResults = true;
  77. /**
  78. * List of map-reduce routines that should be applied over the query
  79. * result
  80. *
  81. * @var array
  82. */
  83. protected $_mapReduce = [];
  84. /**
  85. * List of formatter classes or callbacks that will post-process the
  86. * results when fetched
  87. *
  88. * @var array
  89. */
  90. protected $_formatters = [];
  91. /**
  92. * Holds any custom options passed using applyOptions that could not be processed
  93. * by any method in this class.
  94. *
  95. * @var array
  96. */
  97. protected $_options = [];
  98. /**
  99. * Whether to hydrate results into entity objects
  100. *
  101. * @var boolean
  102. */
  103. protected $_hydrate = true;
  104. /**
  105. * A query cacher instance if this query has caching enabled.
  106. *
  107. * @var Cake\ORM\QueryCacher
  108. */
  109. protected $_cache;
  110. /**
  111. * A callable function that can be used to calculate the total amount of
  112. * records this query will match when not using `limit`
  113. *
  114. * @var callable
  115. */
  116. protected $_counter;
  117. /**
  118. * Instance of a class responsible for storing association containments and
  119. * for eager loading them when this query is executed
  120. *
  121. * @var \Cake\ORM\EagerLoader
  122. */
  123. protected $_eagerLoader;
  124. /**
  125. * Constuctor
  126. *
  127. * @param Cake\Database\Connection $connection
  128. * @param Cake\ORM\Table $table
  129. */
  130. public function __construct($connection, $table) {
  131. $this->connection($connection);
  132. $this->repository($table);
  133. }
  134. /**
  135. * Returns the default table object that will be used by this query,
  136. * that is, the table that will appear in the from clause.
  137. *
  138. * When called with a Table argument, the default table object will be set
  139. * and this query object will be returned for chaining.
  140. *
  141. * @param \Cake\ORM\Table $table The default table object to use
  142. * @return \Cake\ORM\Table|Query
  143. */
  144. public function repository(Table $table = null) {
  145. if ($table === null) {
  146. return $this->_table;
  147. }
  148. $this->_table = $table;
  149. $this->addDefaultTypes($table);
  150. return $this;
  151. }
  152. /**
  153. * Hints this object to associate the correct types when casting conditions
  154. * for the database. This is done by extracting the field types from the schema
  155. * associated to the passed table object. This prevents the user from repeating
  156. * himself when specifying conditions.
  157. *
  158. * This method returns the same query object for chaining.
  159. *
  160. * @param \Cake\ORM\Table $table
  161. * @return Query
  162. */
  163. public function addDefaultTypes(Table $table) {
  164. $alias = $table->alias();
  165. $schema = $table->schema();
  166. $fields = [];
  167. foreach ($schema->columns() as $f) {
  168. $fields[$f] = $fields[$alias . '.' . $f] = $schema->columnType($f);
  169. }
  170. $this->defaultTypes($this->defaultTypes() + $fields);
  171. return $this;
  172. }
  173. /**
  174. * Sets the instance of the eager loader class to use for loading associations
  175. * and storing containments. If called with no arguments, it will return the
  176. * currently configured instance.
  177. *
  178. * @param \Cake\ORM\EagerLoader $instance
  179. * @return \Cake\ORM\Eager|\Cake\ORM\Query
  180. */
  181. public function eagerLoader(EagerLoader $instance = null) {
  182. if ($instance === null) {
  183. if ($this->_eagerLoader === null) {
  184. $this->_eagerLoader = new EagerLoader;
  185. }
  186. return $this->_eagerLoader;
  187. }
  188. $this->_eagerLoader = $instance;
  189. return $this;
  190. }
  191. /**
  192. * Sets the list of associations that should be eagerly loaded along with this
  193. * query. The list of associated tables passed must have been previously set as
  194. * associations using the Table API.
  195. *
  196. * ### Example:
  197. *
  198. * {{{
  199. * // Bring articles' author information
  200. * $query->contain('Author');
  201. *
  202. * // Also bring the category and tags associated to each article
  203. * $query->contain(['Category', 'Tag']);
  204. * }}}
  205. *
  206. * Associations can be arbitrarily nested using dot notation or nested arrays,
  207. * this allows this object to calculate joins or any additional queries that
  208. * must be executed to bring the required associated data.
  209. *
  210. * ### Example:
  211. *
  212. * {{{
  213. * // Eager load the product info, and for each product load other 2 associations
  214. * $query->contain(['Product' => ['Manufacturer', 'Distributor']);
  215. *
  216. * // Which is equivalent to calling
  217. * $query->contain(['Products.Manufactures', 'Products.Distributors']);
  218. *
  219. * // For an author query, load his region, state and country
  220. * $query->contain('Regions.States.Countries');
  221. * }}}
  222. *
  223. * It is possible to control the conditions and fields selected for each of the
  224. * contained associations:
  225. *
  226. * ### Example:
  227. *
  228. * {{{
  229. * $query->contain(['Tags' => function($q) {
  230. * return $q->where(['Tags.is_popular' => true]);
  231. * }]);
  232. *
  233. * $query->contain(['Products.Manufactures' => function($q) {
  234. * return $q->select(['name'])->where(['Manufactures.active' => true]);
  235. * }]);
  236. * }}}
  237. *
  238. * Each association might define special options when eager loaded, the allowed
  239. * options that can be set per association are:
  240. *
  241. * - foreignKey: Used to set a different field to match both tables, if set to false
  242. * no join conditions will be generated automatically
  243. * - fields: An array with the fields that should be fetched from the association
  244. * - queryBuilder: Equivalent to passing a callable instead of an options array
  245. *
  246. * ### Example:
  247. *
  248. * {{{
  249. * // Set options for the articles that will be eagerly loaded for an author
  250. * $query->contain([
  251. * 'Articles' => [
  252. * 'fields' => ['title']
  253. * ]
  254. * ]);
  255. *
  256. * // Use special join conditions for getting an article author's 'likes'
  257. * $query->contain([
  258. * 'Likes' => [
  259. * 'foreignKey' => false,
  260. * 'queryBuilder' => function($q) {
  261. * return $q->where(...); // Add full filtering conditions
  262. * }
  263. * ]
  264. * ]);
  265. *
  266. * If called with no arguments, this function will return an array with
  267. * with the list of previously configured associations to be contained in the
  268. * result.
  269. *
  270. * If called with an empty first argument and $override is set to true, the
  271. * previous list will be emptied.
  272. *
  273. * @param array|string $associations list of table aliases to be queried
  274. * @param boolean $override whether override previous list with the one passed
  275. * defaults to merging previous list with the new one.
  276. * @return array|\Cake\ORM\Query
  277. */
  278. public function contain($associations = null, $override = false) {
  279. if (empty($associations) && $override) {
  280. $this->_eagerLoader = null;
  281. }
  282. $result = $this->eagerLoader()->contain($associations);
  283. if ($associations !== null || $override) {
  284. $this->_dirty();
  285. }
  286. if ($associations === null) {
  287. return $result;
  288. }
  289. return $this;
  290. }
  291. /**
  292. * Adds filtering conditions to this query to only bring rows that have a relation
  293. * to another from an associated table, based on conditions in the associated table.
  294. *
  295. * This function will add entries in the ``contain`` graph.
  296. *
  297. * ### Example:
  298. *
  299. * {{{
  300. * // Bring only articles that were tagged with 'cake'
  301. * $query->matching('Tags', function($q) {
  302. * return $q->where(['name' => 'cake']);
  303. * );
  304. * }}}
  305. *
  306. * It is possible to filter by deep associations by using dot notation:
  307. *
  308. * ### Example:
  309. *
  310. * {{{
  311. * // Bring only articles that were commented by 'markstory'
  312. * $query->matching('Comments.Users', function($q) {
  313. * return $q->where(['username' => 'markstory']);
  314. * );
  315. * }}}
  316. *
  317. * As this function will create ``INNER JOIN``, you might want to consider
  318. * calling ``distinct`` on this query as you might get duplicate rows if
  319. * your conditions don't filter them already. This might be the case, for example,
  320. * of the same user commenting more than once in the same article.
  321. *
  322. * ### Example:
  323. *
  324. * {{{
  325. * // Bring unique articles that were commented by 'markstory'
  326. * $query->distinct(['Articles.id'])
  327. * ->matching('Comments.Users', function($q) {
  328. * return $q->where(['username' => 'markstory']);
  329. * );
  330. * }}}
  331. *
  332. * Please note that the query passed to the closure will only accept calling
  333. * ``select``, ``where``, ``andWhere`` and ``orWhere`` on it. If you wish to
  334. * add more complex clauses you can do it directly in the main query.
  335. *
  336. * @param string $assoc The association to filter by
  337. * @param callable $builder a function that will receive a pre-made query object
  338. * that can be used to add custom conditions or selecting some fields
  339. * @return Query
  340. */
  341. public function matching($assoc, callable $builder = null) {
  342. $this->eagerLoader()->matching($assoc, $builder);
  343. $this->_dirty();
  344. return $this;
  345. }
  346. /**
  347. * Enable/Disable buffered results.
  348. *
  349. * When enabled the ResultSet returned by this Query will be
  350. * buffered. This enables you to iterate a ResultSet multiple times, or
  351. * both cache and iterate the ResultSet.
  352. *
  353. * When disabled it will consume less memory as fetched results are not
  354. * remembered in the ResultSet.
  355. *
  356. * If called with no arguments, it will return whether or not buffering is
  357. * enabled.
  358. *
  359. * @param boolean $enable whether or not to enable buffering
  360. * @return boolean|Query
  361. */
  362. public function bufferResults($enable = null) {
  363. if ($enable === null) {
  364. return $this->_useBufferedResults;
  365. }
  366. $this->_dirty();
  367. $this->_useBufferedResults = (bool)$enable;
  368. return $this;
  369. }
  370. /**
  371. * Set the result set for a query.
  372. *
  373. * Setting the resultset of a query will make execute() a no-op. Instead
  374. * of executing the SQL query and fetching results, the ResultSet provided to this
  375. * method will be returned.
  376. *
  377. * This method is most useful when combined with results stored in a persistent cache.
  378. *
  379. * @param Cake\ORM\ResultSet $results The results this query should return.
  380. * @return Query The query instance.
  381. */
  382. public function setResult($results) {
  383. $this->_results = $results;
  384. return $this;
  385. }
  386. /**
  387. * Enable result caching for this query.
  388. *
  389. * If a query has caching enabled, it will do the following when executed:
  390. *
  391. * - Check the cache for $key. If there are results no SQL will be executed.
  392. * Instead the cached results will be returned.
  393. * - When the cached data is stale/missing the result set will be cached as the query
  394. * is executed.
  395. *
  396. * ## Usage
  397. *
  398. * {{{
  399. * // Simple string key + config
  400. * $query->cache('my_key', 'db_results');
  401. *
  402. * // Function to generate key.
  403. * $query->cache(function($q) {
  404. * $key = serialize($q->clause('select'));
  405. * $key .= serialize($q->clause('where'));
  406. * return md5($key);
  407. * });
  408. *
  409. * // Using a pre-built cache engine.
  410. * $query->cache('my_key', $engine);
  411. *
  412. *
  413. * // Disable caching
  414. * $query->cache(false);
  415. * }}}
  416. *
  417. * @param false|string|Closure $key Either the cache key or a function to generate the cache key.
  418. * When using a function, this query instance will be supplied as an argument.
  419. * @param string|CacheEngine $config Either the name of the cache config to use, or
  420. * a cache config instance.
  421. * @return Query The query instance.
  422. * @throws \RuntimeException When you attempt to cache a non-select query.
  423. */
  424. public function cache($key, $config = 'default') {
  425. if ($this->_type !== 'select' && $this->_type !== null) {
  426. throw new \RuntimeException('You cannot cache the results of non-select queries.');
  427. }
  428. if ($key === false) {
  429. $this->_cache = null;
  430. return $this;
  431. }
  432. $this->_cache = new QueryCacher($key, $config);
  433. return $this;
  434. }
  435. /**
  436. * Executes this query and returns a results iterator. This function is required
  437. * for implementing the IteratorAggregate interface and allows the query to be
  438. * iterated without having to call execute() manually, thus making it look like
  439. * a result set instead of the query itself.
  440. *
  441. * @return Iterator
  442. */
  443. public function getIterator() {
  444. if (empty($this->_iterator) || $this->_dirty) {
  445. $this->_iterator = $this->all();
  446. }
  447. return $this->_iterator;
  448. }
  449. /**
  450. * Fetch the results for this query.
  451. *
  452. * Compiles the SQL representation of this query and executes it using the
  453. * provided connection object. Returns a ResultSet iterator object.
  454. *
  455. * ResultSet is a travesable object that implements the methods found
  456. * on Cake\Collection\Collection.
  457. *
  458. * @return Cake\ORM\ResultCollectionTrait
  459. * @throws RuntimeException if this method is called on a non-select Query.
  460. */
  461. public function all() {
  462. if ($this->_type !== 'select' && $this->_type !== null) {
  463. throw new \RuntimeException(
  464. 'You cannot call all() on a non-select query. Use execute() instead.'
  465. );
  466. }
  467. return $this->getResults();
  468. }
  469. /**
  470. * Get the result set for this query.
  471. *
  472. * Will return either the results set through setResult(), or execute the underlying statement
  473. * and return the ResultSet object ready for streaming of results.
  474. *
  475. * @return Cake\ORM\ResultCollectionTrait
  476. */
  477. public function getResults() {
  478. if (isset($this->_results)) {
  479. return $this->_results;
  480. }
  481. $table = $this->repository();
  482. $event = new Event('Model.beforeFind', $table, [$this, $this->_options]);
  483. $table->getEventManager()->dispatch($event);
  484. if (isset($this->_results)) {
  485. return $this->_results;
  486. }
  487. if ($this->_cache) {
  488. $results = $this->_cache->fetch($this);
  489. }
  490. if (!isset($results)) {
  491. $results = $this->_decorateResults(
  492. new ResultSet($this, $this->execute())
  493. );
  494. if ($this->_cache) {
  495. $this->_cache->store($this, $results);
  496. }
  497. }
  498. $this->_results = $results;
  499. return $this->_results;
  500. }
  501. /**
  502. * Returns an array representation of the results after executing the query.
  503. *
  504. * @return array
  505. */
  506. public function toArray() {
  507. return $this->all()->toArray();
  508. }
  509. /**
  510. * Returns a key => value array representing a single aliased field
  511. * that can be passed directly to the select() method.
  512. * The key will contain the alias and the value the actual field name.
  513. *
  514. * If the field is already aliased, then it will not be changed.
  515. * If no $alias is passed, the default table for this query will be used.
  516. *
  517. * @param string $field
  518. * @param string $alias the alias used to prefix the field
  519. * @return array
  520. */
  521. public function aliasField($field, $alias = null) {
  522. $namespaced = strpos($field, '.') !== false;
  523. $aliasedField = $field;
  524. if ($namespaced) {
  525. list($alias, $field) = explode('.', $field);
  526. }
  527. if (!$alias) {
  528. $alias = $this->repository()->alias();
  529. }
  530. $key = sprintf('%s__%s', $alias, $field);
  531. if (!$namespaced) {
  532. $aliasedField = $alias . '.' . $field;
  533. }
  534. return [$key => $aliasedField];
  535. }
  536. /**
  537. * Runs `aliasfield()` for each field in the provided list and returns
  538. * the result under a single array.
  539. *
  540. * @param array $fields
  541. * @param string $defaultAlias
  542. * @return array
  543. */
  544. public function aliasFields($fields, $defaultAlias = null) {
  545. $aliased = [];
  546. foreach ($fields as $alias => $field) {
  547. if (is_numeric($alias) && is_string($field)) {
  548. $aliased += $this->aliasField($field, $defaultAlias);
  549. continue;
  550. }
  551. $aliased[$alias] = $field;
  552. }
  553. return $aliased;
  554. }
  555. /**
  556. * Populates or adds parts to current query clauses using an array.
  557. * This is handy for passing all query clauses at once.
  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. * @param array $options list of query clauses to apply new parts to. Accepts:
  581. *
  582. * - fields: Maps to the select method
  583. * - conditions: Maps to the where method
  584. * - limit: Maps to the limit method
  585. * - order: Maps to the order method
  586. * - offset: Maps to the offset method
  587. * - group: Maps to the group method
  588. * - having: Maps to the having method
  589. * - contain: Maps to the contain options for eager loading
  590. * - join: Maps to the join method
  591. * - join: Maps to the page method
  592. *
  593. * @return Cake\ORM\Query
  594. */
  595. public function applyOptions(array $options) {
  596. $valid = [
  597. 'fields' => 'select',
  598. 'conditions' => 'where',
  599. 'join' => 'join',
  600. 'order' => 'order',
  601. 'limit' => 'limit',
  602. 'offset' => 'offset',
  603. 'group' => 'group',
  604. 'having' => 'having',
  605. 'contain' => 'contain',
  606. 'page' => 'page',
  607. ];
  608. foreach ($options as $option => $values) {
  609. if (isset($valid[$option]) && isset($values)) {
  610. $this->{$valid[$option]}($values);
  611. } else {
  612. $this->_options[$option] = $values;
  613. }
  614. }
  615. return $this;
  616. }
  617. /**
  618. * Returns an array with the custom options that were applied to this query
  619. * and that were not already processed by another method in this class.
  620. *
  621. * ###Example:
  622. *
  623. * {{{
  624. * $query->applyOptions(['doABarrelRoll' => true, 'fields' => ['id', 'name']);
  625. * $query->getOptions(); // Returns ['doABarrelRoll' => true]
  626. * }}}
  627. *
  628. * @see \Cake\ORM\Query::applyOptions() to read about the options that will
  629. * be processed by this class and not returned by this function
  630. * @return array
  631. */
  632. public function getOptions() {
  633. return $this->_options;
  634. }
  635. /**
  636. * Register a new MapReduce routine to be executed on top of the database results
  637. * Both the mapper and caller callable should be invokable objects.
  638. *
  639. * The MapReduce routing will only be run when the query is executed and the first
  640. * result is attempted to be fetched.
  641. *
  642. * If the first argument is set to null, it will return the list of previously
  643. * registered map reduce routines.
  644. *
  645. * If the third argument is set to true, it will erase previous map reducers
  646. * and replace it with the arguments passed.
  647. *
  648. * @param callable $mapper
  649. * @param callable $reducer
  650. * @param boolean $overwrite
  651. * @return Cake\ORM\Query|array
  652. * @see Cake\Collection\Iterator\MapReduce for details on how to use emit data to the map reducer.
  653. */
  654. public function mapReduce(callable $mapper = null, callable $reducer = null, $overwrite = false) {
  655. if ($overwrite) {
  656. $this->_mapReduce = [];
  657. }
  658. if ($mapper === null) {
  659. return $this->_mapReduce;
  660. }
  661. $this->_mapReduce[] = compact('mapper', 'reducer');
  662. return $this;
  663. }
  664. /**
  665. * Registers a new formatter callback function that is to be executed when the results
  666. * are tried to be fetched from the database.
  667. *
  668. * Formatting callbacks will get as first parameter a `ResultSetDecorator` that
  669. * can be traversed and modified at will. As the second parameter, the formatting
  670. * callback will receive this query instance.
  671. *
  672. * Callbacks are required to return an iterator object, which will be used as
  673. * the return value for this query's result. Formatter functions are applied
  674. * after all the `MapReduce` routines for this query have been executed.
  675. *
  676. * If the first argument is set to null, it will return the list of previously
  677. * registered map reduce routines.
  678. *
  679. * If the second argument is set to true, it will erase previous formatters
  680. * and replace them with the passed first argument.
  681. *
  682. * ### Example:
  683. *
  684. * {{{
  685. * //Return all results from the table indexed by id
  686. * $query->select(['id', 'name'])->formatResults(function($results, $query) {
  687. * return $results->indexBy('id');
  688. * });
  689. *
  690. * //Add a new column to the ResultSet
  691. * $query->select(['name', 'birth_date'])->formatResults(function($results, $query) {
  692. * return $results->map(function($row) {
  693. * $row['age'] = $row['birth_date']->diff(new DateTime)->y;
  694. * return $row;
  695. * });
  696. * });
  697. * }}}
  698. *
  699. * @param callable $formatter
  700. * @param boolean|integer $mode
  701. * @return Cake\ORM\Query|array
  702. */
  703. public function formatResults(callable $formatter = null, $mode = self::APPEND) {
  704. if ($mode === self::OVERWRITE) {
  705. $this->_formatters = [];
  706. }
  707. if ($formatter === null) {
  708. return $this->_formatters;
  709. }
  710. if ($mode === self::PREPEND) {
  711. array_unshift($this->_formatters, $formatter);
  712. return $this;
  713. }
  714. $this->_formatters[] = $formatter;
  715. return $this;
  716. }
  717. /**
  718. * Returns the first result out of executing this query, if the query has not been
  719. * executed before, it will set the limit clause to 1 for performance reasons.
  720. *
  721. * ### Example:
  722. *
  723. * `$singleUser = $query->select(['id', 'username'])->first();`
  724. *
  725. * @return mixed the first result from the ResultSet
  726. */
  727. public function first() {
  728. if ($this->_dirty) {
  729. $this->limit(1);
  730. }
  731. $this->_results = $this->all();
  732. return $this->_results->first();
  733. }
  734. /**
  735. * Return the COUNT(*) for for the query.
  736. *
  737. * @return integer
  738. */
  739. public function count() {
  740. $query = clone $this;
  741. $query->limit(null);
  742. $query->offset(null);
  743. $query->mapReduce(null, null, true);
  744. $query->formatResults(null, true);
  745. $counter = $this->_counter;
  746. if ($counter) {
  747. $query->counter(null);
  748. return (int)$counter($query);
  749. }
  750. $count = ['count' => $query->func()->count('*')];
  751. if (!count($query->clause('group')) && !$query->clause('distinct')) {
  752. $statement = $query
  753. ->select($count, true)
  754. ->execute();
  755. } else {
  756. // Forcing at least one field to be selected
  757. $query->select($query->newExpr()->add('1'));
  758. $statement = $this->connection()->newQuery()
  759. ->select($count)
  760. ->from(['count_source' => $query])
  761. ->execute();
  762. }
  763. $result = $statement->fetch('assoc')['count'];
  764. $statement->closeCursor();
  765. return (int)$result;
  766. }
  767. /**
  768. * Registers a callable function that will be executed when the `count` method in
  769. * this query is called. The return value for the function will be set as the
  770. * return value of the `count` method.
  771. *
  772. * This is particularly useful when you need to optimize a query for returning the
  773. * count, for example removing unnecessary joins, removing group by or just return
  774. * an estimated number of rows.
  775. *
  776. * The callback will receive as first argument a clone of this query and not this
  777. * query itself.
  778. *
  779. * @param callable $counter
  780. * @return Cake\ORM\Query
  781. */
  782. public function counter($counter) {
  783. $this->_counter = $counter;
  784. return $this;
  785. }
  786. /**
  787. * Toggle hydrating entites.
  788. *
  789. * If set to false array results will be returned
  790. *
  791. * @param boolean|null $enable Use a boolean to set the hydration mode.
  792. * Null will fetch the current hydration mode.
  793. * @return boolean|Query A boolean when reading, and $this when setting the mode.
  794. */
  795. public function hydrate($enable = null) {
  796. if ($enable === null) {
  797. return $this->_hydrate;
  798. }
  799. $this->_dirty();
  800. $this->_hydrate = (bool)$enable;
  801. return $this;
  802. }
  803. /**
  804. * Decorates the ResultSet iterator with MapReduce routines
  805. *
  806. * @param $result Cake\ORM\ResultCollectionTrait original results
  807. * @return Cake\ORM\ResultCollectionTrait
  808. */
  809. protected function _decorateResults($result) {
  810. foreach ($this->_mapReduce as $functions) {
  811. $result = new MapReduce($result, $functions['mapper'], $functions['reducer']);
  812. }
  813. if (!empty($this->_mapReduce)) {
  814. $result = new ResultSetDecorator($result);
  815. }
  816. foreach ($this->_formatters as $formatter) {
  817. $result = $formatter($result, $this);
  818. }
  819. if (!empty($this->_formatters) && !($result instanceof ResultSetDecorator)) {
  820. $result = new ResultSetDecorator($result);
  821. }
  822. return $result;
  823. }
  824. /**
  825. * Auxiliary function used to wrap the original statement from the driver with
  826. * any registered callbacks. This will also setup the correct statement class
  827. * in order to eager load deep associations.
  828. *
  829. * @param Cake\Database\Statement $statement to be decorated
  830. * @return Cake\Database\Statement
  831. */
  832. protected function _decorateStatement($statement) {
  833. $statement = parent::_decorateStatement($statement);
  834. return $this->eagerLoader()->loadExternal($this, $statement);
  835. }
  836. /**
  837. * Applies some defaults to the query object before it is executed.
  838. *
  839. * Specifically add the FROM clause, adds default table fields if none are
  840. * specified and applies the joins required to eager load associations defined
  841. * using `contain`
  842. *
  843. * @see Cake\Database\Query::execute()
  844. * @return Query
  845. */
  846. protected function _transformQuery() {
  847. if (!$this->_dirty) {
  848. return parent::_transformQuery();
  849. }
  850. if ($this->_type === 'select') {
  851. if (empty($this->_parts['from'])) {
  852. $this->from([$this->_table->alias() => $this->_table->table()]);
  853. }
  854. $this->_addDefaultFields();
  855. $this->eagerLoader()->attachAssociations($this, !$this->_hasFields);
  856. }
  857. return parent::_transformQuery();
  858. }
  859. /**
  860. * Inspects if there are any set fields for selecting, otherwise adds all
  861. * the fields for the default table.
  862. *
  863. * @return void
  864. */
  865. protected function _addDefaultFields() {
  866. $select = $this->clause('select');
  867. $this->_hasFields = true;
  868. if (!count($select)) {
  869. $this->_hasFields = false;
  870. $this->select($this->repository()->schema()->columns());
  871. $select = $this->clause('select');
  872. }
  873. $aliased = $this->aliasFields($select, $this->repository()->alias());
  874. $this->select($aliased, true);
  875. }
  876. /**
  877. * Apply custom finds to against an existing query object.
  878. *
  879. * Allows custom find methods to be combined and applied to each other.
  880. *
  881. * {{{
  882. * $table->find('all')->find('recent');
  883. * }}}
  884. *
  885. * The above is an example of stacking multiple finder methods onto
  886. * a single query.
  887. *
  888. * @param string $finder The finder method to use.
  889. * @param array $options The options for the finder.
  890. * @return Cake\ORM\Query Returns a modified query.
  891. * @see Cake\ORM\Table::find()
  892. */
  893. public function find($finder, $options = []) {
  894. return $this->repository()->callFinder($finder, $this, $options);
  895. }
  896. /**
  897. * Marks a query as dirty, removing any preprocessed information
  898. * from in memory caching such as previous results
  899. *
  900. * @return void
  901. */
  902. protected function _dirty() {
  903. $this->_results = null;
  904. parent::_dirty();
  905. }
  906. /**
  907. * Create an update query.
  908. *
  909. * This changes the query type to be 'update'.
  910. * Can be combined with set() and where() methods to create update queries.
  911. *
  912. * @param string $table Unused parameter.
  913. * @return Query
  914. */
  915. public function update($table = null) {
  916. $table = $this->repository()->table();
  917. return parent::update($table);
  918. }
  919. /**
  920. * Create a delete query.
  921. *
  922. * This changes the query type to be 'delete'.
  923. * Can be combined with the where() method to create delete queries.
  924. *
  925. * @param string $table Unused parameter.
  926. * @return Query
  927. */
  928. public function delete($table = null) {
  929. $table = $this->repository()->table();
  930. return parent::delete($table);
  931. }
  932. /**
  933. * Create an insert query.
  934. *
  935. * This changes the query type to be 'insert'.
  936. * Note calling this method will reset any data previously set
  937. * with Query::values()
  938. *
  939. * Can be combined with the where() method to create delete queries.
  940. *
  941. * @param array $columns The columns to insert into.
  942. * @param array $types A map between columns & their datatypes.
  943. * @return Query
  944. */
  945. public function insert($columns, $types = []) {
  946. $table = $this->repository()->table();
  947. $this->into($table);
  948. return parent::insert($columns, $types);
  949. }
  950. /**
  951. * Enables calling methods from the ResultSet as if they were from this class
  952. *
  953. * @param string $method the method to call
  954. * @param array $arguments list of arguments for the method to call
  955. * @return mixed
  956. * @throws \BadMethodCallException if no such method exists in ResultSet
  957. */
  958. public function __call($method, $arguments) {
  959. if ($this->type() === 'select') {
  960. $resultSetClass = __NAMESPACE__ . '\ResultSetDecorator';
  961. if (in_array($method, get_class_methods($resultSetClass))) {
  962. $results = $this->all();
  963. return call_user_func_array([$results, $method], $arguments);
  964. }
  965. }
  966. throw new \BadMethodCallException(
  967. sprintf('Unknown method "%s"', $method)
  968. );
  969. }
  970. }