Query.php 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340
  1. <?php
  2. /**
  3. * PHP Version 5.4
  4. *
  5. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  6. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  7. *
  8. * Licensed under The MIT License
  9. * For full copyright and license information, please see the LICENSE.txt
  10. * Redistributions of files must retain the above copyright notice.
  11. *
  12. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  13. * @link http://cakephp.org CakePHP(tm) Project
  14. * @since CakePHP(tm) v 3.0.0
  15. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  16. */
  17. namespace Cake\ORM;
  18. use Cake\Collection\Iterator\MapReduce;
  19. use Cake\Database\Query as DatabaseQuery;
  20. use Cake\Database\Statement\BufferedStatement;
  21. use Cake\Database\Statement\CallbackStatement;
  22. use Cake\Event\Event;
  23. use Cake\ORM\QueryCacher;
  24. use Cake\ORM\Table;
  25. /**
  26. * Extends the base Query class to provide new methods related to association
  27. * loading, automatic fields selection, automatic type casting and to wrap results
  28. * into an specific iterator that will be responsible for hydrating results if
  29. * required.
  30. *
  31. */
  32. class Query extends DatabaseQuery {
  33. /**
  34. * Indicates that the operation should append to the list
  35. *
  36. * @var integer
  37. */
  38. const APPEND = 0;
  39. /**
  40. * Indicates that the operation should prepend to the list
  41. *
  42. * @var integer
  43. */
  44. const PREPEND = 1;
  45. /**
  46. * Indicates that the operation should overwrite the list
  47. *
  48. * @var boolean
  49. */
  50. const OVERWRITE = true;
  51. /**
  52. * Instance of a table object this query is bound to
  53. *
  54. * @var \Cake\ORM\Table
  55. */
  56. protected $_table;
  57. /**
  58. * Nested array describing the association to be fetched
  59. * and the options to apply for each of them, if any
  60. *
  61. * @var \ArrayObject
  62. */
  63. protected $_containments;
  64. /**
  65. * Contains a nested array with the compiled containments tree
  66. * This is a normalized version of the user provided containments array.
  67. *
  68. * @var array
  69. */
  70. protected $_normalizedContainments;
  71. /**
  72. * Whether the user select any fields before being executed, this is used
  73. * to determined if any fields should be automatically be selected.
  74. *
  75. * @var boolean
  76. */
  77. protected $_hasFields;
  78. /**
  79. * A list of associations that should be eagerly loaded
  80. *
  81. * @var array
  82. */
  83. protected $_loadEagerly = [];
  84. /**
  85. * List of options accepted by associations in contain()
  86. * index by key for faster access
  87. *
  88. * @var array
  89. */
  90. protected $_containOptions = [
  91. 'associations' => 1,
  92. 'foreignKey' => 1,
  93. 'conditions' => 1,
  94. 'fields' => 1,
  95. 'sort' => 1,
  96. 'matching' => 1,
  97. 'queryBuilder' => 1
  98. ];
  99. /**
  100. * A ResultSet.
  101. *
  102. * When set, query execution will be bypassed.
  103. *
  104. * @var Cake\ORM\ResultSet
  105. * @see setResult()
  106. */
  107. protected $_results;
  108. /**
  109. * Boolean for tracking whether or not buffered results
  110. * are enabled.
  111. *
  112. * @var boolean
  113. */
  114. protected $_useBufferedResults = true;
  115. /**
  116. * List of map-reduce routines that should be applied over the query
  117. * result
  118. *
  119. * @var array
  120. */
  121. protected $_mapReduce = [];
  122. /**
  123. * List of formatter classes or callbacks that will post-process the
  124. * results when fetched
  125. *
  126. * @var array
  127. */
  128. protected $_formatters = [];
  129. /**
  130. * Holds any custom options passed using applyOptions that could not be processed
  131. * by any method in this class.
  132. *
  133. * @var array
  134. */
  135. protected $_options = [];
  136. /**
  137. * Whether to hydrate results into entity objects
  138. *
  139. * @var boolean
  140. */
  141. protected $_hydrate = true;
  142. /**
  143. * A query cacher instance if this query has caching enabled.
  144. *
  145. * @var Cake\ORM\QueryCacher
  146. */
  147. protected $_cache;
  148. /**
  149. * A callable function that can be used to calculate the total amount of
  150. * records this query will match when not using `limit`
  151. *
  152. * @var callable
  153. */
  154. protected $_counter;
  155. /**
  156. * Constuctor
  157. *
  158. * @param Cake\Database\Connection $connection
  159. * @param Cake\ORM\Table $table
  160. */
  161. public function __construct($connection, $table) {
  162. $this->connection($connection);
  163. $this->repository($table);
  164. }
  165. /**
  166. * Returns the default table object that will be used by this query,
  167. * that is, the table that will appear in the from clause.
  168. *
  169. * When called with a Table argument, the default table object will be set
  170. * and this query object will be returned for chaining.
  171. *
  172. * @param \Cake\ORM\Table $table The default table object to use
  173. * @return \Cake\ORM\Table|Query
  174. */
  175. public function repository(Table $table = null) {
  176. if ($table === null) {
  177. return $this->_table;
  178. }
  179. $this->_table = $table;
  180. $this->addDefaultTypes($table);
  181. return $this;
  182. }
  183. /**
  184. * Hints this object to associate the correct types when casting conditions
  185. * for the database. This is done by extracting the field types from the schema
  186. * associated to the passed table object. This prevents the user from repeating
  187. * himself when specifying conditions.
  188. *
  189. * This method returns the same query object for chaining.
  190. *
  191. * @param \Cake\ORM\Table $table
  192. * @return Query
  193. */
  194. public function addDefaultTypes(Table $table) {
  195. $alias = $table->alias();
  196. $schema = $table->schema();
  197. $fields = [];
  198. foreach ($schema->columns() as $f) {
  199. $fields[$f] = $fields[$alias . '.' . $f] = $schema->columnType($f);
  200. }
  201. $this->defaultTypes($this->defaultTypes() + $fields);
  202. return $this;
  203. }
  204. /**
  205. * Sets the list of associations that should be eagerly loaded along with this
  206. * query. The list of associated tables passed must have been previously set as
  207. * associations using the Table API.
  208. *
  209. * ### Example:
  210. *
  211. * {{{
  212. * // Bring articles' author information
  213. * $query->contain('Author');
  214. *
  215. * // Also bring the category and tags associated to each article
  216. * $query->contain(['Category', 'Tag']);
  217. * }}}
  218. *
  219. * Associations can be arbitrarily nested using dot notation or nested arrays,
  220. * this allows this object to calculate joins or any additional queries that
  221. * must be executed to bring the required associated data.
  222. *
  223. * ### Example:
  224. *
  225. * {{{
  226. * // Eager load the product info, and for each product load other 2 associations
  227. * $query->contain(['Product' => ['Manufacturer', 'Distributor']);
  228. *
  229. * // Which is equivalent to calling
  230. * $query->contain(['Products.Manufactures', 'Products.Distributors']);
  231. *
  232. * // For an author query, load his region, state and country
  233. * $query->contain('Regions.States.Countries');
  234. * }}}
  235. *
  236. * It is possible to control the conditions and fields selected for each of the
  237. * contained associations:
  238. *
  239. * ### Example:
  240. *
  241. * {{{
  242. * $query->contain(['Tags' => function($q) {
  243. * return $q->where(['Tags.is_popular' => true]);
  244. * }]);
  245. *
  246. * $query->contain(['Products.Manufactures' => function($q) {
  247. * return $q->select(['name'])->where(['Manufactures.active' => true]);
  248. * }]);
  249. * }}}
  250. *
  251. * Each association might define special options when eager loaded, the allowed
  252. * options that can be set per association are:
  253. *
  254. * - foreignKey: Used to set a different field to match both tables, if set to false
  255. * no join conditions will be generated automatically
  256. * - fields: An array with the fields that should be fetched from the association
  257. * - queryBuilder: Equivalent to passing a callable instead of an options array
  258. *
  259. * ### Example:
  260. *
  261. * {{{
  262. * // Set options for the articles that will be eagerly loaded for an author
  263. * $query->contain([
  264. * 'Articles' => [
  265. * 'fields' => ['title']
  266. * ]
  267. * ]);
  268. *
  269. * // Use special join conditions for getting an article author's 'likes'
  270. * $query->contain([
  271. * 'Likes' => [
  272. * 'foreignKey' => false,
  273. * 'queryBuilder' => function($q) {
  274. * return $q->where(...); // Add full filtering conditions
  275. * }
  276. * ]
  277. * ]);
  278. *
  279. * If called with no arguments, this function will return an ArrayObject with
  280. * with the list of previously configured associations to be contained in the
  281. * result. This object can be modified directly as the reference is kept inside
  282. * the query.
  283. *
  284. * The resulting ArrayObject will always have association aliases as keys, and
  285. * options as values, if no options are passed, the values will be set to an empty
  286. * array
  287. *
  288. * Please note that when modifying directly the containments array, you are
  289. * required to maintain the structure. That is, association names as keys
  290. * having array values. Failing to do so will result in an error
  291. *
  292. * If called with an empty first argument and $override is set to true, the
  293. * previous list will be emptied.
  294. *
  295. * @param array|string $associations list of table aliases to be queried
  296. * @param boolean $override whether override previous list with the one passed
  297. * defaults to merging previous list with the new one.
  298. * @return \ArrayObject|Query
  299. */
  300. public function contain($associations = null, $override = false) {
  301. if ($this->_containments === null || $override) {
  302. $this->_dirty();
  303. $this->_containments = new \ArrayObject;
  304. }
  305. if ($associations === null) {
  306. return $this->_containments;
  307. }
  308. $associations = (array)$associations;
  309. $current = current($associations);
  310. if (is_array($current) && isset($current['instance'])) {
  311. $this->_containments = $this->_normalizedContainments = $associations;
  312. return $this;
  313. }
  314. $old = $this->_containments->getArrayCopy();
  315. $associations = $this->_reformatContain($associations, $old);
  316. $this->_containments->exchangeArray($associations);
  317. $this->_normalizedContainments = null;
  318. $this->_dirty();
  319. return $this;
  320. }
  321. /**
  322. * Adds filtering conditions to this query to only bring rows that have a relation
  323. * to another from an associated table, based on conditions in the associated table.
  324. *
  325. * This function will add entries in the ``contain`` graph.
  326. *
  327. * ### Example:
  328. *
  329. * {{{
  330. * // Bring only articles that were tagged with 'cake'
  331. * $query->matching('Tags', function($q) {
  332. * return $q->where(['name' => 'cake']);
  333. * );
  334. * }}}
  335. *
  336. * It is possible to filter by deep associations by using dot notation:
  337. *
  338. * ### Example:
  339. *
  340. * {{{
  341. * // Bring only articles that were commented by 'markstory'
  342. * $query->matching('Comments.Users', function($q) {
  343. * return $q->where(['username' => 'markstory']);
  344. * );
  345. * }}}
  346. *
  347. * As this function will create ``INNER JOIN``, you might want to consider
  348. * calling ``distinct`` on this query as you might get duplicate rows if
  349. * your conditions don't filter them already. This might be the case, for example,
  350. * of the same user commenting more than once in the same article.
  351. *
  352. * ### Example:
  353. *
  354. * {{{
  355. * // Bring unique articles that were commented by 'markstory'
  356. * $query->distinct(['Articles.id'])
  357. * ->matching('Comments.Users', function($q) {
  358. * return $q->where(['username' => 'markstory']);
  359. * );
  360. * }}}
  361. *
  362. * Please note that the query passed to the closure will only accept calling
  363. * ``select``, ``where``, ``andWhere`` and ``orWhere`` on it. If you wish to
  364. * add more complex clauses you can do it directly in the main query.
  365. *
  366. * @param string $assoc The association to filter by
  367. * @param callable $builder a function that will receive a pre-made query object
  368. * that can be used to add custom conditions or selecting some fields
  369. * @return Query
  370. */
  371. public function matching($assoc, callable $builder = null) {
  372. $assocs = explode('.', $assoc);
  373. $last = array_pop($assocs);
  374. $containments = [];
  375. $pointer =& $containments;
  376. foreach ($assocs as $name) {
  377. $pointer[$name] = ['matching' => true];
  378. $pointer =& $pointer[$name];
  379. }
  380. $pointer[$last] = ['queryBuilder' => $builder, 'matching' => true];
  381. return $this->contain($containments);
  382. }
  383. /**
  384. * Formats the containments array so that associations are always set as keys
  385. * in the array. This function merges the original associations array with
  386. * the new associations provided
  387. *
  388. * @param array $associations user provided containments array
  389. * @param array $original The original containments array to merge
  390. * with the new one
  391. * @return array
  392. */
  393. protected function _reformatContain($associations, $original) {
  394. $result = $original;
  395. foreach ((array)$associations as $table => $options) {
  396. $pointer =& $result;
  397. if (is_int($table)) {
  398. $table = $options;
  399. $options = [];
  400. }
  401. if (isset($this->_containOptions[$table])) {
  402. $pointer[$table] = $options;
  403. continue;
  404. }
  405. if (strpos($table, '.')) {
  406. $path = explode('.', $table);
  407. $table = array_pop($path);
  408. foreach ($path as $t) {
  409. $pointer += [$t => []];
  410. $pointer =& $pointer[$t];
  411. }
  412. }
  413. if (is_array($options)) {
  414. $options = $this->_reformatContain($options, []);
  415. }
  416. if ($options instanceof \Closure) {
  417. $options = ['queryBuilder' => $options];
  418. }
  419. $pointer += [$table => []];
  420. $pointer[$table] = $options + $pointer[$table];
  421. }
  422. return $result;
  423. }
  424. /**
  425. * Returns the fully normalized array of associations that should be eagerly
  426. * loaded. The normalized array will restructure the original one by sorting
  427. * all associations under one key and special options under another.
  428. *
  429. * Additionally it will set an 'instance' key per association containing the
  430. * association instance from the corresponding source table
  431. *
  432. * @return array
  433. */
  434. public function normalizedContainments() {
  435. if ($this->_normalizedContainments !== null || empty($this->_containments)) {
  436. return $this->_normalizedContainments;
  437. }
  438. $contain = [];
  439. foreach ($this->_containments as $table => $options) {
  440. if (!empty($options['instance'])) {
  441. $contain = (array)$this->_containments;
  442. break;
  443. }
  444. $contain[$table] = $this->_normalizeContain(
  445. $this->_table,
  446. $table,
  447. $options
  448. );
  449. }
  450. return $this->_normalizedContainments = $contain;
  451. }
  452. /**
  453. * Enable/Disable buffered results.
  454. *
  455. * When enabled the ResultSet returned by this Query will be
  456. * buffered. This enables you to iterate a ResultSet multiple times, or
  457. * both cache and iterate the ResultSet.
  458. *
  459. * When disabled it will consume less memory as fetched results are not
  460. * remembered in the ResultSet.
  461. *
  462. * If called with no arguments, it will return whether or not buffering is
  463. * enabled.
  464. *
  465. * @param boolean $enable whether or not to enable buffering
  466. * @return boolean|Query
  467. */
  468. public function bufferResults($enable = null) {
  469. if ($enable === null) {
  470. return $this->_useBufferedResults;
  471. }
  472. $this->_dirty();
  473. $this->_useBufferedResults = (bool)$enable;
  474. return $this;
  475. }
  476. /**
  477. * Set the result set for a query.
  478. *
  479. * Setting the resultset of a query will make execute() a no-op. Instead
  480. * of executing the SQL query and fetching results, the ResultSet provided to this
  481. * method will be returned.
  482. *
  483. * This method is most useful when combined with results stored in a persistent cache.
  484. *
  485. * @param Cake\ORM\ResultSet $results The results this query should return.
  486. * @return Query The query instance.
  487. */
  488. public function setResult($results) {
  489. $this->_results = $results;
  490. return $this;
  491. }
  492. /**
  493. * Enable result caching for this query.
  494. *
  495. * If a query has caching enabled, it will do the following when executed:
  496. *
  497. * - Check the cache for $key. If there are results no SQL will be executed.
  498. * Instead the cached results will be returned.
  499. * - When the cached data is stale/missing the result set will be cached as the query
  500. * is executed.
  501. *
  502. * ## Usage
  503. *
  504. * {{{
  505. * // Simple string key + config
  506. * $query->cache('my_key', 'db_results');
  507. *
  508. * // Function to generate key.
  509. * $query->cache(function($q) {
  510. * $key = serialize($q->clause('select'));
  511. * $key .= serialize($q->clause('where'));
  512. * return md5($key);
  513. * });
  514. *
  515. * // Using a pre-built cache engine.
  516. * $query->cache('my_key', $engine);
  517. *
  518. *
  519. * // Disable caching
  520. * $query->cache(false);
  521. * }}}
  522. *
  523. * @param false|string|Closure $key Either the cache key or a function to generate the cache key.
  524. * When using a function, this query instance will be supplied as an argument.
  525. * @param string|CacheEngine $config Either the name of the cache config to use, or
  526. * a cache config instance.
  527. * @return Query The query instance.
  528. * @throws \RuntimeException When you attempt to cache a non-select query.
  529. */
  530. public function cache($key, $config = 'default') {
  531. if ($this->_type !== 'select' && $this->_type !== null) {
  532. throw new \RuntimeException('You cannot cache the results of non-select queries.');
  533. }
  534. if ($key === false) {
  535. $this->_cache = null;
  536. return $this;
  537. }
  538. $this->_cache = new QueryCacher($key, $config);
  539. return $this;
  540. }
  541. /**
  542. * Executes this query and returns a results iterator. This function is required
  543. * for implementing the IteratorAggregate interface and allows the query to be
  544. * iterated without having to call execute() manually, thus making it look like
  545. * a result set instead of the query itself.
  546. *
  547. * @return Iterator
  548. */
  549. public function getIterator() {
  550. if (empty($this->_iterator) || $this->_dirty) {
  551. $this->_iterator = $this->all();
  552. }
  553. return $this->_iterator;
  554. }
  555. /**
  556. * Fetch the results for this query.
  557. *
  558. * Compiles the SQL representation of this query and executes it using the
  559. * provided connection object. Returns a ResultSet iterator object.
  560. *
  561. * ResultSet is a travesable object that implements the methods found
  562. * on Cake\Collection\Collection.
  563. *
  564. * @return Cake\ORM\ResultCollectionTrait
  565. * @throws RuntimeException if this method is called on a non-select Query.
  566. */
  567. public function all() {
  568. if ($this->_type !== 'select' && $this->_type !== null) {
  569. throw new \RuntimeException(
  570. 'You cannot call all() on a non-select query. Use execute() instead.'
  571. );
  572. }
  573. return $this->getResults();
  574. }
  575. /**
  576. * Get the result set for this query.
  577. *
  578. * Will return either the results set through setResult(), or execute the underlying statement
  579. * and return the ResultSet object ready for streaming of results.
  580. *
  581. * @return Cake\ORM\ResultCollectionTrait
  582. */
  583. public function getResults() {
  584. if (isset($this->_results)) {
  585. return $this->_results;
  586. }
  587. $table = $this->repository();
  588. $event = new Event('Model.beforeFind', $table, [$this, $this->_options]);
  589. $table->getEventManager()->dispatch($event);
  590. if (isset($this->_results)) {
  591. return $this->_results;
  592. }
  593. if ($this->_cache) {
  594. $results = $this->_cache->fetch($this);
  595. }
  596. if (!isset($results)) {
  597. $results = $this->_decorateResults(
  598. new ResultSet($this, $this->execute())
  599. );
  600. if ($this->_cache) {
  601. $this->_cache->store($this, $results);
  602. }
  603. }
  604. $this->_results = $results;
  605. return $this->_results;
  606. }
  607. /**
  608. * Returns an array representation of the results after executing the query.
  609. *
  610. * @return array
  611. */
  612. public function toArray() {
  613. return $this->all()->toArray();
  614. }
  615. /**
  616. * Returns a key => value array representing a single aliased field
  617. * that can be passed directly to the select() method.
  618. * The key will contain the alias and the value the actual field name.
  619. *
  620. * If the field is already aliased, then it will not be changed.
  621. * If no $alias is passed, the default table for this query will be used.
  622. *
  623. * @param string $field
  624. * @param string $alias the alias used to prefix the field
  625. * @return array
  626. */
  627. public function aliasField($field, $alias = null) {
  628. $namespaced = strpos($field, '.') !== false;
  629. $aliasedField = $field;
  630. if ($namespaced) {
  631. list($alias, $field) = explode('.', $field);
  632. }
  633. if (!$alias) {
  634. $alias = $this->repository()->alias();
  635. }
  636. $key = sprintf('%s__%s', $alias, $field);
  637. if (!$namespaced) {
  638. $aliasedField = $alias . '.' . $field;
  639. }
  640. return [$key => $aliasedField];
  641. }
  642. /**
  643. * Runs `aliasfield()` for each field in the provided list and returns
  644. * the result under a single array.
  645. *
  646. * @param array $fields
  647. * @param string $defaultAlias
  648. * @return array
  649. */
  650. public function aliasFields($fields, $defaultAlias = null) {
  651. $aliased = [];
  652. foreach ($fields as $alias => $field) {
  653. if (is_numeric($alias) && is_string($field)) {
  654. $aliased += $this->aliasField($field, $defaultAlias);
  655. continue;
  656. }
  657. $aliased[$alias] = $field;
  658. }
  659. return $aliased;
  660. }
  661. /**
  662. * Populates or adds parts to current query clauses using an array.
  663. * This is handy for passing all query clauses at once.
  664. *
  665. * ## Example:
  666. *
  667. * {{{
  668. * $query->applyOptions([
  669. * 'fields' => ['id', 'name'],
  670. * 'conditions' => [
  671. * 'created >=' => '2013-01-01'
  672. * ],
  673. * 'limit' => 10
  674. * ]);
  675. * }}}
  676. *
  677. * Is equivalent to:
  678. *
  679. * {{{
  680. * $query
  681. * ->select(['id', 'name'])
  682. * ->where(['created >=' => '2013-01-01'])
  683. * ->limit(10)
  684. * }}}
  685. *
  686. * @param array $options list of query clauses to apply new parts to. Accepts:
  687. *
  688. * - fields: Maps to the select method
  689. * - conditions: Maps to the where method
  690. * - limit: Maps to the limit method
  691. * - order: Maps to the order method
  692. * - offset: Maps to the offset method
  693. * - group: Maps to the group method
  694. * - having: Maps to the having method
  695. * - contain: Maps to the contain options for eager loading
  696. * - join: Maps to the join method
  697. * - join: Maps to the page method
  698. *
  699. * @return Cake\ORM\Query
  700. */
  701. public function applyOptions(array $options) {
  702. $valid = [
  703. 'fields' => 'select',
  704. 'conditions' => 'where',
  705. 'join' => 'join',
  706. 'order' => 'order',
  707. 'limit' => 'limit',
  708. 'offset' => 'offset',
  709. 'group' => 'group',
  710. 'having' => 'having',
  711. 'contain' => 'contain',
  712. 'page' => 'page',
  713. ];
  714. foreach ($options as $option => $values) {
  715. if (isset($valid[$option]) && isset($values)) {
  716. $this->{$valid[$option]}($values);
  717. } else {
  718. $this->_options[$option] = $values;
  719. }
  720. }
  721. return $this;
  722. }
  723. /**
  724. * Returns an array with the custom options that were applied to this query
  725. * and that were not already processed by another method in this class.
  726. *
  727. * ###Example:
  728. *
  729. * {{{
  730. * $query->applyOptions(['doABarrelRoll' => true, 'fields' => ['id', 'name']);
  731. * $query->getOptions(); // Returns ['doABarrelRoll' => true]
  732. * }}}
  733. *
  734. * @see \Cake\ORM\Query::applyOptions() to read about the options that will
  735. * be processed by this class and not returned by this function
  736. * @return array
  737. */
  738. public function getOptions() {
  739. return $this->_options;
  740. }
  741. /**
  742. * Register a new MapReduce routine to be executed on top of the database results
  743. * Both the mapper and caller callable should be invokable objects.
  744. *
  745. * The MapReduce routing will only be run when the query is executed and the first
  746. * result is attempted to be fetched.
  747. *
  748. * If the first argument is set to null, it will return the list of previously
  749. * registered map reduce routines.
  750. *
  751. * If the third argument is set to true, it will erase previous map reducers
  752. * and replace it with the arguments passed.
  753. *
  754. * @param callable $mapper
  755. * @param callable $reducer
  756. * @param boolean $overwrite
  757. * @return Cake\ORM\Query|array
  758. * @see Cake\Collection\Iterator\MapReduce for details on how to use emit data to the map reducer.
  759. */
  760. public function mapReduce(callable $mapper = null, callable $reducer = null, $overwrite = false) {
  761. if ($overwrite) {
  762. $this->_mapReduce = [];
  763. }
  764. if ($mapper === null) {
  765. return $this->_mapReduce;
  766. }
  767. $this->_mapReduce[] = compact('mapper', 'reducer');
  768. return $this;
  769. }
  770. /**
  771. * Registers a new formatter callback function that is to be executed when the results
  772. * are tried to be fetched from the database.
  773. *
  774. * Formatting callbacks will get as first parameter a `ResultSetDecorator` that
  775. * can be traversed and modified at will. As the second parameter, the formatting
  776. * callback will receive this query instance.
  777. *
  778. * Callbacks are required to return an iterator object, which will be used as
  779. * the return value for this query's result. Formatter functions are applied
  780. * after all the `MapReduce` routines for this query have been executed.
  781. *
  782. * If the first argument is set to null, it will return the list of previously
  783. * registered map reduce routines.
  784. *
  785. * If the second argument is set to true, it will erase previous formatters
  786. * and replace them with the passed first argument.
  787. *
  788. * ### Example:
  789. *
  790. * {{{
  791. * //Return all results from the table indexed by id
  792. * $query->select(['id', 'name'])->formatResults(function($results, $query) {
  793. * return $results->indexBy('id');
  794. * });
  795. *
  796. * //Add a new column to the ResultSet
  797. * $query->select(['name', 'birth_date'])->formatResults(function($results, $query) {
  798. * return $results->map(function($row) {
  799. * $row['age'] = $row['birth_date']->diff(new DateTime)->y;
  800. * return $row;
  801. * });
  802. * });
  803. * }}}
  804. *
  805. * @param callable $formatter
  806. * @param boolean|integer $mode
  807. * @return Cake\ORM\Query|array
  808. */
  809. public function formatResults(callable $formatter = null, $mode = self::APPEND) {
  810. if ($mode === self::OVERWRITE) {
  811. $this->_formatters = [];
  812. }
  813. if ($formatter === null) {
  814. return $this->_formatters;
  815. }
  816. if ($mode === self::PREPEND) {
  817. array_unshift($this->_formatters, $formatter);
  818. return $this;
  819. }
  820. $this->_formatters[] = $formatter;
  821. return $this;
  822. }
  823. /**
  824. * Returns the first result out of executing this query, if the query has not been
  825. * executed before, it will set the limit clause to 1 for performance reasons.
  826. *
  827. * ### Example:
  828. *
  829. * `$singleUser = $query->select(['id', 'username'])->first();`
  830. *
  831. * @return mixed the first result from the ResultSet
  832. */
  833. public function first() {
  834. if ($this->_dirty) {
  835. $this->limit(1);
  836. }
  837. $this->_results = $this->all();
  838. return $this->_results->first();
  839. }
  840. /**
  841. * Return the COUNT(*) for for the query.
  842. *
  843. * @return integer
  844. */
  845. public function count() {
  846. $query = clone $this;
  847. $query->limit(null);
  848. $query->offset(null);
  849. $query->mapReduce(null, null, true);
  850. $query->formatResults(null, true);
  851. $counter = $this->_counter;
  852. if ($counter) {
  853. $query->counter(null);
  854. return (int)$counter($query);
  855. }
  856. $count = ['count' => $query->func()->count('*')];
  857. if (!count($query->clause('group')) && !$query->clause('distinct')) {
  858. $statement = $query
  859. ->select($count, true)
  860. ->execute();
  861. } else {
  862. // Forcing at least one field to be selected
  863. $query->select($query->newExpr()->add('1'));
  864. $statement = $this->connection()->newQuery()
  865. ->select($count)
  866. ->from(['count_source' => $query])
  867. ->execute();
  868. }
  869. $result = $statement->fetch('assoc')['count'];
  870. $statement->closeCursor();
  871. return (int)$result;
  872. }
  873. /**
  874. * Registers a callable function that will be executed when the `count` method in
  875. * this query is called. The return value for the function will be set as the
  876. * return value of the `count` method.
  877. *
  878. * This is particularly useful when you need to optimize a query for returning the
  879. * count, for example removing unnecessary joins, removing group by or just return
  880. * an estimated number of rows.
  881. *
  882. * The callback will receive as first argument a clone of this query and not this
  883. * query itself.
  884. *
  885. * @param callable $counter
  886. * @return Cake\ORM\Query
  887. */
  888. public function counter($counter) {
  889. $this->_counter = $counter;
  890. return $this;
  891. }
  892. /**
  893. * Toggle hydrating entites.
  894. *
  895. * If set to false array results will be returned
  896. *
  897. * @param boolean|null $enable Use a boolean to set the hydration mode.
  898. * Null will fetch the current hydration mode.
  899. * @return boolean|Query A boolean when reading, and $this when setting the mode.
  900. */
  901. public function hydrate($enable = null) {
  902. if ($enable === null) {
  903. return $this->_hydrate;
  904. }
  905. $this->_dirty();
  906. $this->_hydrate = (bool)$enable;
  907. return $this;
  908. }
  909. /**
  910. * Decorates the ResultSet iterator with MapReduce routines
  911. *
  912. * @param $result Cake\ORM\ResultCollectionTrait original results
  913. * @return Cake\ORM\ResultCollectionTrait
  914. */
  915. protected function _decorateResults($result) {
  916. foreach ($this->_mapReduce as $functions) {
  917. $result = new MapReduce($result, $functions['mapper'], $functions['reducer']);
  918. }
  919. if (!empty($this->_mapReduce)) {
  920. $result = new ResultSetDecorator($result);
  921. }
  922. foreach ($this->_formatters as $formatter) {
  923. $result = $formatter($result, $this);
  924. }
  925. if (!empty($this->_formatters) && !($result instanceof ResultSetDecorator)) {
  926. $result = new ResultSetDecorator($result);
  927. }
  928. return $result;
  929. }
  930. /**
  931. * Auxiliary function used to wrap the original statement from the driver with
  932. * any registered callbacks. This will also setup the correct statement class
  933. * in order to eager load deep associations.
  934. *
  935. * @param Cake\Database\Statement $statement to be decorated
  936. * @return Cake\Database\Statement
  937. */
  938. protected function _decorateStatement($statement) {
  939. $statement = parent::_decorateStatement($statement);
  940. if ($this->_loadEagerly) {
  941. if (!($statement instanceof BufferedStatement)) {
  942. $statement = new BufferedStatement($statement, $this->connection()->driver());
  943. }
  944. $statement = $this->_eagerLoad($statement);
  945. }
  946. return $statement;
  947. }
  948. /**
  949. * Applies some defaults to the query object before it is executed.
  950. *
  951. * Specifically add the FROM clause, adds default table fields if none are
  952. * specified and applies the joins required to eager load associations defined
  953. * using `contain`
  954. *
  955. * @see Cake\Database\Query::execute()
  956. * @return Query
  957. */
  958. protected function _transformQuery() {
  959. if (!$this->_dirty) {
  960. return parent::_transformQuery();
  961. }
  962. if ($this->_type === 'select') {
  963. if (empty($this->_parts['from'])) {
  964. $this->from([$this->_table->alias() => $this->_table->table()]);
  965. }
  966. $this->_addDefaultFields();
  967. $this->_addContainments();
  968. }
  969. return parent::_transformQuery();
  970. }
  971. /**
  972. * Helper function used to add the required joins for associations defined using
  973. * `contain()`
  974. *
  975. * @return void
  976. */
  977. protected function _addContainments() {
  978. $this->_loadEagerly = [];
  979. if (empty($this->_containments)) {
  980. return;
  981. }
  982. $contain = $this->normalizedContainments();
  983. foreach ($contain as $relation => $meta) {
  984. if ($meta['instance'] && !$meta['canBeJoined']) {
  985. $this->_loadEagerly[$relation] = $meta;
  986. }
  987. }
  988. foreach ($this->_resolveJoins($this->_table, $contain) as $options) {
  989. $table = $options['instance']->target();
  990. $this->_addJoin($options['instance'], $options['config']);
  991. foreach ($options['associations'] as $relation => $meta) {
  992. if ($meta['instance'] && !$meta['canBeJoined']) {
  993. $this->_loadEagerly[$relation] = $meta;
  994. }
  995. }
  996. }
  997. }
  998. /**
  999. * Auxiliary function responsible for fully normalizing deep associations defined
  1000. * using `contain()`
  1001. *
  1002. * @param Table $parent owning side of the association
  1003. * @param string $alias name of the association to be loaded
  1004. * @param array $options list of extra options to use for this association
  1005. * @return array normalized associations
  1006. * @throws \InvalidArgumentException When containments refer to associations that do not exist.
  1007. */
  1008. protected function _normalizeContain(Table $parent, $alias, $options) {
  1009. $defaults = $this->_containOptions;
  1010. $instance = $parent->association($alias);
  1011. if (!$instance) {
  1012. throw new \InvalidArgumentException(
  1013. sprintf('%s is not associated with %s', $parent->alias(), $alias)
  1014. );
  1015. }
  1016. $table = $instance->target();
  1017. $extra = array_diff_key($options, $defaults);
  1018. $config = [
  1019. 'associations' => [],
  1020. 'instance' => $instance,
  1021. 'config' => array_diff_key($options, $extra)
  1022. ];
  1023. $config['canBeJoined'] = $instance->canBeJoined($config['config']);
  1024. foreach ($extra as $t => $assoc) {
  1025. $config['associations'][$t] = $this->_normalizeContain($table, $t, $assoc);
  1026. }
  1027. return $config;
  1028. }
  1029. /**
  1030. * Helper function used to compile a list of all associations that can be
  1031. * joined in this query.
  1032. *
  1033. * @param Table $source the owning side of the association
  1034. * @param array $associations list of associations for $source
  1035. * @return array
  1036. */
  1037. protected function _resolveJoins($source, $associations) {
  1038. $result = [];
  1039. foreach ($associations as $table => $options) {
  1040. $associated = $options['instance'];
  1041. if ($options['canBeJoined']) {
  1042. $result[$table] = $options;
  1043. $result += $this->_resolveJoins($associated->target(), $options['associations']);
  1044. }
  1045. }
  1046. return $result;
  1047. }
  1048. /**
  1049. * Adds a join based on a particular association and some custom options
  1050. *
  1051. * @param Association $association
  1052. * @param array $options
  1053. * @return void
  1054. */
  1055. protected function _addJoin($association, $options) {
  1056. $association->attachTo($this, $options + ['includeFields' => !$this->_hasFields]);
  1057. }
  1058. /**
  1059. * Helper method that will calculate those associations that cannot be joined
  1060. * directly in this query and will setup the required extra queries for fetching
  1061. * the extra data.
  1062. *
  1063. * @param Statement $statement original query statement
  1064. * @return CallbackStatement $statement modified statement with extra loaders
  1065. */
  1066. protected function _eagerLoad($statement) {
  1067. $collected = $this->_collectKeys($statement);
  1068. foreach ($this->_loadEagerly as $meta) {
  1069. $contain = $meta['associations'];
  1070. $alias = $meta['instance']->source()->alias();
  1071. $keys = isset($collected[$alias]) ? $collected[$alias] : null;
  1072. $f = $meta['instance']->eagerLoader(
  1073. $meta['config'] + ['query' => $this, 'contain' => $contain, 'keys' => $keys]
  1074. );
  1075. $statement = new CallbackStatement($statement, $this->connection()->driver(), $f);
  1076. }
  1077. return $statement;
  1078. }
  1079. /**
  1080. * Helper function used to return the keys from the query records that will be used
  1081. * to eagerly load associations.
  1082. *
  1083. *
  1084. * @param BufferedStatement $statement
  1085. * @return array
  1086. */
  1087. protected function _collectKeys($statement) {
  1088. $collectKeys = [];
  1089. foreach ($this->_loadEagerly as $meta) {
  1090. $source = $meta['instance']->source();
  1091. if ($meta['instance']->requiresKeys($meta['config'])) {
  1092. $alias = $source->alias();
  1093. $pkFields = [];
  1094. foreach ((array)$source->primaryKey() as $key) {
  1095. $pkFields[] = key($this->aliasField($key, $alias));
  1096. }
  1097. $collectKeys[$alias] = [$alias, $pkFields, count($pkFields) === 1];
  1098. }
  1099. }
  1100. $keys = [];
  1101. if (!empty($collectKeys)) {
  1102. while ($result = $statement->fetch('assoc')) {
  1103. foreach ($collectKeys as $parts) {
  1104. if ($parts[2]) {
  1105. $keys[$parts[0]][] = $result[$parts[1][0]];
  1106. continue;
  1107. }
  1108. $collected = [];
  1109. foreach ($parts[1] as $key) {
  1110. $collected[] = $result[$key];
  1111. }
  1112. $keys[$parts[0]][] = $collected;
  1113. }
  1114. }
  1115. $statement->rewind();
  1116. }
  1117. return $keys;
  1118. }
  1119. /**
  1120. * Inspects if there are any set fields for selecting, otherwise adds all
  1121. * the fields for the default table.
  1122. *
  1123. * @return void
  1124. */
  1125. protected function _addDefaultFields() {
  1126. $select = $this->clause('select');
  1127. $this->_hasFields = true;
  1128. if (!count($select)) {
  1129. $this->_hasFields = false;
  1130. $this->select($this->repository()->schema()->columns());
  1131. $select = $this->clause('select');
  1132. }
  1133. $aliased = $this->aliasFields($select, $this->repository()->alias());
  1134. $this->select($aliased, true);
  1135. }
  1136. /**
  1137. * Apply custom finds to against an existing query object.
  1138. *
  1139. * Allows custom find methods to be combined and applied to each other.
  1140. *
  1141. * {{{
  1142. * $table->find('all')->find('recent');
  1143. * }}}
  1144. *
  1145. * The above is an example of stacking multiple finder methods onto
  1146. * a single query.
  1147. *
  1148. * @param string $finder The finder method to use.
  1149. * @param array $options The options for the finder.
  1150. * @return Cake\ORM\Query Returns a modified query.
  1151. * @see Cake\ORM\Table::find()
  1152. */
  1153. public function find($finder, $options = []) {
  1154. return $this->repository()->callFinder($finder, $this, $options);
  1155. }
  1156. /**
  1157. * Marks a query as dirty, removing any preprocessed information
  1158. * from in memory caching such as previous results
  1159. *
  1160. * @return void
  1161. */
  1162. protected function _dirty() {
  1163. $this->_results = null;
  1164. parent::_dirty();
  1165. }
  1166. /**
  1167. * Create an update query.
  1168. *
  1169. * This changes the query type to be 'update'.
  1170. * Can be combined with set() and where() methods to create update queries.
  1171. *
  1172. * @param string $table Unused parameter.
  1173. * @return Query
  1174. */
  1175. public function update($table = null) {
  1176. $table = $this->repository()->table();
  1177. return parent::update($table);
  1178. }
  1179. /**
  1180. * Create a delete query.
  1181. *
  1182. * This changes the query type to be 'delete'.
  1183. * Can be combined with the where() method to create delete queries.
  1184. *
  1185. * @param string $table Unused parameter.
  1186. * @return Query
  1187. */
  1188. public function delete($table = null) {
  1189. $table = $this->repository()->table();
  1190. return parent::delete($table);
  1191. }
  1192. /**
  1193. * Create an insert query.
  1194. *
  1195. * This changes the query type to be 'insert'.
  1196. * Note calling this method will reset any data previously set
  1197. * with Query::values()
  1198. *
  1199. * Can be combined with the where() method to create delete queries.
  1200. *
  1201. * @param array $columns The columns to insert into.
  1202. * @param array $types A map between columns & their datatypes.
  1203. * @return Query
  1204. */
  1205. public function insert($columns, $types = []) {
  1206. $table = $this->repository()->table();
  1207. $this->into($table);
  1208. return parent::insert($columns, $types);
  1209. }
  1210. /**
  1211. * Enables calling methods from the ResultSet as if they were from this class
  1212. *
  1213. * @param string $method the method to call
  1214. * @param array $arguments list of arguments for the method to call
  1215. * @return mixed
  1216. * @throws \BadMethodCallException if no such method exists in ResultSet
  1217. */
  1218. public function __call($method, $arguments) {
  1219. if ($this->type() === 'select') {
  1220. $resultSetClass = __NAMESPACE__ . '\ResultSetDecorator';
  1221. if (in_array($method, get_class_methods($resultSetClass))) {
  1222. $results = $this->all();
  1223. return call_user_func_array([$results, $method], $arguments);
  1224. }
  1225. }
  1226. throw new \BadMethodCallException(
  1227. sprintf('Unknown method "%s"', $method)
  1228. );
  1229. }
  1230. }