Query.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  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 Cake\Database\Query as DatabaseQuery;
  17. use Cake\Database\ValueBinder;
  18. use Cake\Datasource\QueryTrait;
  19. use Cake\ORM\EagerLoader;
  20. use Cake\ORM\ResultSet;
  21. use Cake\ORM\Table;
  22. use JsonSerializable;
  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. * Hints this object to associate the correct types when casting conditions
  112. * for the database. This is done by extracting the field types from the schema
  113. * associated to the passed table object. This prevents the user from repeating
  114. * himself when specifying conditions.
  115. *
  116. * This method returns the same query object for chaining.
  117. *
  118. * @param \Cake\ORM\Table $table The table to pull types from
  119. * @return $this
  120. */
  121. public function addDefaultTypes(Table $table)
  122. {
  123. $alias = $table->alias();
  124. $schema = $table->schema();
  125. $fields = [];
  126. foreach ($schema->columns() as $f) {
  127. $fields[$f] = $fields[$alias . '.' . $f] = $schema->columnType($f);
  128. }
  129. $this->defaultTypes($fields);
  130. return $this;
  131. }
  132. /**
  133. * Sets the instance of the eager loader class to use for loading associations
  134. * and storing containments. If called with no arguments, it will return the
  135. * currently configured instance.
  136. *
  137. * @param \Cake\ORM\EagerLoader $instance The eager loader to use. Pass null
  138. * to get the current eagerloader.
  139. * @return \Cake\ORM\EagerLoader|$this
  140. */
  141. public function eagerLoader(EagerLoader $instance = null)
  142. {
  143. if ($instance === null) {
  144. if ($this->_eagerLoader === null) {
  145. $this->_eagerLoader = new EagerLoader;
  146. }
  147. return $this->_eagerLoader;
  148. }
  149. $this->_eagerLoader = $instance;
  150. return $this;
  151. }
  152. /**
  153. * Sets the list of associations that should be eagerly loaded along with this
  154. * query. The list of associated tables passed must have been previously set as
  155. * associations using the Table API.
  156. *
  157. * ### Example:
  158. *
  159. * ```
  160. * // Bring articles' author information
  161. * $query->contain('Author');
  162. *
  163. * // Also bring the category and tags associated to each article
  164. * $query->contain(['Category', 'Tag']);
  165. * ```
  166. *
  167. * Associations can be arbitrarily nested using dot notation or nested arrays,
  168. * this allows this object to calculate joins or any additional queries that
  169. * must be executed to bring the required associated data.
  170. *
  171. * ### Example:
  172. *
  173. * ```
  174. * // Eager load the product info, and for each product load other 2 associations
  175. * $query->contain(['Product' => ['Manufacturer', 'Distributor']);
  176. *
  177. * // Which is equivalent to calling
  178. * $query->contain(['Products.Manufactures', 'Products.Distributors']);
  179. *
  180. * // For an author query, load his region, state and country
  181. * $query->contain('Regions.States.Countries');
  182. * ```
  183. *
  184. * It is possible to control the conditions and fields selected for each of the
  185. * contained associations:
  186. *
  187. * ### Example:
  188. *
  189. * ```
  190. * $query->contain(['Tags' => function ($q) {
  191. * return $q->where(['Tags.is_popular' => true]);
  192. * }]);
  193. *
  194. * $query->contain(['Products.Manufactures' => function ($q) {
  195. * return $q->select(['name'])->where(['Manufactures.active' => true]);
  196. * }]);
  197. * ```
  198. *
  199. * Each association might define special options when eager loaded, the allowed
  200. * options that can be set per association are:
  201. *
  202. * - foreignKey: Used to set a different field to match both tables, if set to false
  203. * no join conditions will be generated automatically. `false` can only be used on
  204. * joinable associations and cannot be used with hasMany or belongsToMany associations.
  205. * - fields: An array with the fields that should be fetched from the association
  206. * - queryBuilder: Equivalent to passing a callable instead of an options array
  207. *
  208. * ### Example:
  209. *
  210. * ```
  211. * // Set options for the hasMany articles that will be eagerly loaded for an author
  212. * $query->contain([
  213. * 'Articles' => [
  214. * 'fields' => ['title', 'author_id']
  215. * ]
  216. * ]);
  217. * ```
  218. *
  219. * When containing associations, it is important to include foreign key columns.
  220. * Failing to do so will trigger exceptions.
  221. *
  222. * ```
  223. * // Use special join conditions for getting an Articles's belongsTo 'authors'
  224. * $query->contain([
  225. * 'Authors' => [
  226. * 'foreignKey' => false,
  227. * 'queryBuilder' => function ($q) {
  228. * return $q->where(...); // Add full filtering conditions
  229. * }
  230. * ]
  231. * ]);
  232. * ```
  233. *
  234. * If called with no arguments, this function will return an array with
  235. * with the list of previously configured associations to be contained in the
  236. * result.
  237. *
  238. * If called with an empty first argument and $override is set to true, the
  239. * previous list will be emptied.
  240. *
  241. * @param array|string $associations list of table aliases to be queried
  242. * @param bool $override whether override previous list with the one passed
  243. * defaults to merging previous list with the new one.
  244. * @return array|$this
  245. */
  246. public function contain($associations = null, $override = false)
  247. {
  248. if (empty($associations) && $override) {
  249. $this->_eagerLoader = null;
  250. }
  251. $result = $this->eagerLoader()->contain($associations);
  252. if ($associations !== null || $override) {
  253. $this->_dirty();
  254. }
  255. if ($associations === null) {
  256. return $result;
  257. }
  258. return $this;
  259. }
  260. /**
  261. * Adds filtering conditions to this query to only bring rows that have a relation
  262. * to another from an associated table, based on conditions in the associated table.
  263. *
  264. * This function will add entries in the `contain` graph.
  265. *
  266. * ### Example:
  267. *
  268. * ```
  269. * // Bring only articles that were tagged with 'cake'
  270. * $query->matching('Tags', function ($q) {
  271. * return $q->where(['name' => 'cake']);
  272. * );
  273. * ```
  274. *
  275. * It is possible to filter by deep associations by using dot notation:
  276. *
  277. * ### Example:
  278. *
  279. * ```
  280. * // Bring only articles that were commented by 'markstory'
  281. * $query->matching('Comments.Users', function ($q) {
  282. * return $q->where(['username' => 'markstory']);
  283. * );
  284. * ```
  285. *
  286. * As this function will create `INNER JOIN`, you might want to consider
  287. * calling `distinct` on this query as you might get duplicate rows if
  288. * your conditions don't filter them already. This might be the case, for example,
  289. * of the same user commenting more than once in the same article.
  290. *
  291. * ### Example:
  292. *
  293. * ```
  294. * // Bring unique articles that were commented by 'markstory'
  295. * $query->distinct(['Articles.id'])
  296. * ->matching('Comments.Users', function ($q) {
  297. * return $q->where(['username' => 'markstory']);
  298. * );
  299. * ```
  300. *
  301. * Please note that the query passed to the closure will only accept calling
  302. * `select`, `where`, `andWhere` and `orWhere` on it. If you wish to
  303. * add more complex clauses you can do it directly in the main query.
  304. *
  305. * @param string $assoc The association to filter by
  306. * @param callable $builder a function that will receive a pre-made query object
  307. * that can be used to add custom conditions or selecting some fields
  308. * @return $this
  309. */
  310. public function matching($assoc, callable $builder = null)
  311. {
  312. $this->eagerLoader()->matching($assoc, $builder);
  313. $this->_dirty();
  314. return $this;
  315. }
  316. /**
  317. * Returns a key => value array representing a single aliased field
  318. * that can be passed directly to the select() method.
  319. * The key will contain the alias and the value the actual field name.
  320. *
  321. * If the field is already aliased, then it will not be changed.
  322. * If no $alias is passed, the default table for this query will be used.
  323. *
  324. * @param string $field The field to alias
  325. * @param string $alias the alias used to prefix the field
  326. * @return array
  327. */
  328. public function aliasField($field, $alias = null)
  329. {
  330. $namespaced = strpos($field, '.') !== false;
  331. $aliasedField = $field;
  332. if ($namespaced) {
  333. list($alias, $field) = explode('.', $field);
  334. }
  335. if (!$alias) {
  336. $alias = $this->repository()->alias();
  337. }
  338. $key = sprintf('%s__%s', $alias, $field);
  339. if (!$namespaced) {
  340. $aliasedField = $alias . '.' . $field;
  341. }
  342. return [$key => $aliasedField];
  343. }
  344. /**
  345. * Runs `aliasField()` for each field in the provided list and returns
  346. * the result under a single array.
  347. *
  348. * @param array $fields The fields to alias
  349. * @param string|null $defaultAlias The default alias
  350. * @return array
  351. */
  352. public function aliasFields($fields, $defaultAlias = null)
  353. {
  354. $aliased = [];
  355. foreach ($fields as $alias => $field) {
  356. if (is_numeric($alias) && is_string($field)) {
  357. $aliased += $this->aliasField($field, $defaultAlias);
  358. continue;
  359. }
  360. $aliased[$alias] = $field;
  361. }
  362. return $aliased;
  363. }
  364. /**
  365. * Populates or adds parts to current query clauses using an array.
  366. * This is handy for passing all query clauses at once. The option array accepts:
  367. *
  368. * - fields: Maps to the select method
  369. * - conditions: Maps to the where method
  370. * - limit: Maps to the limit method
  371. * - order: Maps to the order method
  372. * - offset: Maps to the offset method
  373. * - group: Maps to the group method
  374. * - having: Maps to the having method
  375. * - contain: Maps to the contain options for eager loading
  376. * - join: Maps to the join method
  377. * - page: Maps to the page method
  378. *
  379. * ### Example:
  380. *
  381. * ```
  382. * $query->applyOptions([
  383. * 'fields' => ['id', 'name'],
  384. * 'conditions' => [
  385. * 'created >=' => '2013-01-01'
  386. * ],
  387. * 'limit' => 10
  388. * ]);
  389. * ```
  390. *
  391. * Is equivalent to:
  392. *
  393. * ```
  394. * $query
  395. * ->select(['id', 'name'])
  396. * ->where(['created >=' => '2013-01-01'])
  397. * ->limit(10)
  398. * ```
  399. *
  400. * @param array $options list of query clauses to apply new parts to.
  401. * @return $this
  402. */
  403. public function applyOptions(array $options)
  404. {
  405. $valid = [
  406. 'fields' => 'select',
  407. 'conditions' => 'where',
  408. 'join' => 'join',
  409. 'order' => 'order',
  410. 'limit' => 'limit',
  411. 'offset' => 'offset',
  412. 'group' => 'group',
  413. 'having' => 'having',
  414. 'contain' => 'contain',
  415. 'page' => 'page',
  416. ];
  417. ksort($options);
  418. foreach ($options as $option => $values) {
  419. if (isset($valid[$option]) && isset($values)) {
  420. $this->{$valid[$option]}($values);
  421. } else {
  422. $this->_options[$option] = $values;
  423. }
  424. }
  425. return $this;
  426. }
  427. /**
  428. * Creates a copy of this current query, triggers beforeFind and resets some state.
  429. *
  430. * The following state will be cleared:
  431. *
  432. * - autoFields
  433. * - limit
  434. * - offset
  435. * - map/reduce functions
  436. * - result formatters
  437. * - order
  438. * - containments
  439. *
  440. * This method creates query clones that are useful when working with subqueries.
  441. *
  442. * @return \Cake\ORM\Query
  443. */
  444. public function cleanCopy()
  445. {
  446. $query = clone $this;
  447. $query->triggerBeforeFind();
  448. $query->autoFields(false);
  449. $query->limit(null);
  450. $query->order([], true);
  451. $query->offset(null);
  452. $query->mapReduce(null, null, true);
  453. $query->formatResults(null, true);
  454. return $query;
  455. }
  456. /**
  457. * Returns the COUNT(*) for the query.
  458. *
  459. * @return int
  460. */
  461. public function count()
  462. {
  463. $query = $this->cleanCopy();
  464. $counter = $this->_counter;
  465. if ($counter) {
  466. $query->counter(null);
  467. return (int)$counter($query);
  468. }
  469. $count = ['count' => $query->func()->count('*')];
  470. $complex = count($query->clause('group')) || $query->clause('distinct');
  471. $complex = $complex || count($query->clause('union'));
  472. if (!$complex) {
  473. $statement = $query
  474. ->select($count, true)
  475. ->autoFields(false)
  476. ->execute();
  477. } else {
  478. $statement = $this->connection()->newQuery()
  479. ->select($count)
  480. ->from(['count_source' => $query])
  481. ->execute();
  482. }
  483. $result = $statement->fetch('assoc')['count'];
  484. $statement->closeCursor();
  485. return (int)$result;
  486. }
  487. /**
  488. * Registers a callable function that will be executed when the `count` method in
  489. * this query is called. The return value for the function will be set as the
  490. * return value of the `count` method.
  491. *
  492. * This is particularly useful when you need to optimize a query for returning the
  493. * count, for example removing unnecessary joins, removing group by or just return
  494. * an estimated number of rows.
  495. *
  496. * The callback will receive as first argument a clone of this query and not this
  497. * query itself.
  498. *
  499. * @param callable $counter The counter value
  500. * @return $this
  501. */
  502. public function counter($counter)
  503. {
  504. $this->_counter = $counter;
  505. return $this;
  506. }
  507. /**
  508. * Toggle hydrating entities.
  509. *
  510. * If set to false array results will be returned
  511. *
  512. * @param bool|null $enable Use a boolean to set the hydration mode.
  513. * Null will fetch the current hydration mode.
  514. * @return bool|$this A boolean when reading, and $this when setting the mode.
  515. */
  516. public function hydrate($enable = null)
  517. {
  518. if ($enable === null) {
  519. return $this->_hydrate;
  520. }
  521. $this->_dirty();
  522. $this->_hydrate = (bool)$enable;
  523. return $this;
  524. }
  525. /**
  526. * {@inheritDoc}
  527. *
  528. * @return $this
  529. * @throws \RuntimeException When you attempt to cache a non-select query.
  530. */
  531. public function cache($key, $config = 'default')
  532. {
  533. if ($this->_type !== 'select' && $this->_type !== null) {
  534. throw new \RuntimeException('You cannot cache the results of non-select queries.');
  535. }
  536. return $this->_cache($key, $config);
  537. }
  538. /**
  539. * {@inheritDoc}
  540. *
  541. * @throws \RuntimeException if this method is called on a non-select Query.
  542. */
  543. public function all()
  544. {
  545. if ($this->_type !== 'select' && $this->_type !== null) {
  546. throw new \RuntimeException(
  547. 'You cannot call all() on a non-select query. Use execute() instead.'
  548. );
  549. }
  550. return $this->_all();
  551. }
  552. /**
  553. * Trigger the beforeFind event on the query's repository object.
  554. *
  555. * Will not trigger more than once, and only for select queries.
  556. *
  557. * @return void
  558. */
  559. public function triggerBeforeFind()
  560. {
  561. if (!$this->_beforeFindFired && $this->_type === 'select') {
  562. $table = $this->repository();
  563. $table->dispatchEvent('Model.beforeFind', [$this, $this->_options, !$this->eagerLoaded()]);
  564. $this->_beforeFindFired = true;
  565. }
  566. }
  567. /**
  568. * {@inheritDoc}
  569. */
  570. public function sql(ValueBinder $binder = null)
  571. {
  572. $this->triggerBeforeFind();
  573. $this->_transformQuery();
  574. $sql = parent::sql($binder);
  575. return $sql;
  576. }
  577. /**
  578. * Executes this query and returns a ResultSet object containing the results.
  579. * This will also setup the correct statement class in order to eager load deep
  580. * associations.
  581. *
  582. * @return \Cake\ORM\ResultSet
  583. */
  584. protected function _execute()
  585. {
  586. $this->triggerBeforeFind();
  587. if ($this->_results) {
  588. $decorator = $this->_decoratorClass();
  589. return new $decorator($this->_results);
  590. }
  591. $statement = $this->eagerLoader()->loadExternal($this, $this->execute());
  592. return new ResultSet($this, $statement);
  593. }
  594. /**
  595. * Applies some defaults to the query object before it is executed.
  596. *
  597. * Specifically add the FROM clause, adds default table fields if none are
  598. * specified and applies the joins required to eager load associations defined
  599. * using `contain`
  600. *
  601. * @see \Cake\Database\Query::execute()
  602. * @return $this
  603. */
  604. protected function _transformQuery()
  605. {
  606. if (!$this->_dirty) {
  607. return;
  608. }
  609. if ($this->_type === 'select') {
  610. if (empty($this->_parts['from'])) {
  611. $this->from([$this->_repository->alias() => $this->_repository->table()]);
  612. }
  613. $this->_addDefaultFields();
  614. $this->eagerLoader()->attachAssociations($this, $this->_repository, !$this->_hasFields);
  615. }
  616. }
  617. /**
  618. * Inspects if there are any set fields for selecting, otherwise adds all
  619. * the fields for the default table.
  620. *
  621. * @return void
  622. */
  623. protected function _addDefaultFields()
  624. {
  625. $select = $this->clause('select');
  626. $this->_hasFields = true;
  627. if (!count($select) || $this->_autoFields === true) {
  628. $this->_hasFields = false;
  629. $this->select($this->repository()->schema()->columns());
  630. $select = $this->clause('select');
  631. }
  632. $aliased = $this->aliasFields($select, $this->repository()->alias());
  633. $this->select($aliased, true);
  634. }
  635. /**
  636. * Apply custom finds to against an existing query object.
  637. *
  638. * Allows custom find methods to be combined and applied to each other.
  639. *
  640. * ```
  641. * $table->find('all')->find('recent');
  642. * ```
  643. *
  644. * The above is an example of stacking multiple finder methods onto
  645. * a single query.
  646. *
  647. * @param string $finder The finder method to use.
  648. * @param array $options The options for the finder.
  649. * @return $this Returns a modified query.
  650. * @see \Cake\ORM\Table::find()
  651. */
  652. public function find($finder, array $options = [])
  653. {
  654. return $this->repository()->callFinder($finder, $this, $options);
  655. }
  656. /**
  657. * Marks a query as dirty, removing any preprocessed information
  658. * from in memory caching such as previous results
  659. *
  660. * @return void
  661. */
  662. protected function _dirty()
  663. {
  664. $this->_results = null;
  665. parent::_dirty();
  666. }
  667. /**
  668. * Create an update query.
  669. *
  670. * This changes the query type to be 'update'.
  671. * Can be combined with set() and where() methods to create update queries.
  672. *
  673. * @param string|null $table Unused parameter.
  674. * @return $this
  675. */
  676. public function update($table = null)
  677. {
  678. $table = $this->repository()->table();
  679. return parent::update($table);
  680. }
  681. /**
  682. * Create a delete query.
  683. *
  684. * This changes the query type to be 'delete'.
  685. * Can be combined with the where() method to create delete queries.
  686. *
  687. * @param string|null $table Unused parameter.
  688. * @return $this
  689. */
  690. public function delete($table = null)
  691. {
  692. $repo = $this->repository();
  693. $this->from([$repo->alias() => $repo->table()]);
  694. return parent::delete();
  695. }
  696. /**
  697. * Create an insert query.
  698. *
  699. * This changes the query type to be 'insert'.
  700. * Note calling this method will reset any data previously set
  701. * with Query::values()
  702. *
  703. * Can be combined with the where() method to create delete queries.
  704. *
  705. * @param array $columns The columns to insert into.
  706. * @param array $types A map between columns & their datatypes.
  707. * @return $this
  708. */
  709. public function insert(array $columns, array $types = [])
  710. {
  711. $table = $this->repository()->table();
  712. $this->into($table);
  713. return parent::insert($columns, $types);
  714. }
  715. /**
  716. * {@inheritDoc}
  717. *
  718. * @throws \BadMethodCallException if the method is called for a non-select query
  719. */
  720. public function __call($method, $arguments)
  721. {
  722. if ($this->type() === 'select') {
  723. return $this->_call($method, $arguments);
  724. }
  725. throw new \BadMethodCallException(
  726. sprintf('Cannot call method "%s" on a "%s" query', $method, $this->type())
  727. );
  728. }
  729. /**
  730. * {@inheritDoc}
  731. */
  732. public function __debugInfo()
  733. {
  734. $eagerLoader = $this->eagerLoader();
  735. return parent::__debugInfo() + [
  736. 'hydrate' => $this->_hydrate,
  737. 'buffered' => $this->_useBufferedResults,
  738. 'formatters' => count($this->_formatters),
  739. 'mapReducers' => count($this->_mapReduce),
  740. 'contain' => $eagerLoader->contain(),
  741. 'matching' => $eagerLoader->matching(),
  742. 'extraOptions' => $this->_options,
  743. 'repository' => $this->_repository
  744. ];
  745. }
  746. /**
  747. * Executes the query and converts the result set into JSON.
  748. *
  749. * Part of JsonSerializable interface.
  750. *
  751. * @return \Cake\Datasource\ResultSetInterface The data to convert to JSON.
  752. */
  753. public function jsonSerialize()
  754. {
  755. return $this->all();
  756. }
  757. /**
  758. * Get/Set whether or not the ORM should automatically append fields.
  759. *
  760. * By default calling select() will disable auto-fields. You can re-enable
  761. * auto-fields with this method.
  762. *
  763. * @param bool|null $value The value to set or null to read the current value.
  764. * @return bool|$this Either the current value or the query object.
  765. */
  766. public function autoFields($value = null)
  767. {
  768. if ($value === null) {
  769. return $this->_autoFields;
  770. }
  771. $this->_autoFields = (bool)$value;
  772. return $this;
  773. }
  774. /**
  775. * Decorates the results iterator with MapReduce routines and formatters
  776. *
  777. * @param \Traversable $result Original results
  778. * @return \Cake\Datasource\ResultSetInterface
  779. */
  780. protected function _decorateResults($result)
  781. {
  782. $result = $this->_applyDecorators($result);
  783. if (!($result instanceof ResultSet) && $this->bufferResults()) {
  784. $class = $this->_decoratorClass();
  785. $result = new $class($result->buffered());
  786. }
  787. return $result;
  788. }
  789. }