PaginatorComponent.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 2.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Controller\Component;
  16. use Cake\Controller\Component;
  17. use Cake\Controller\ComponentRegistry;
  18. use Cake\Datasource\Exception\PageOutOfBoundsException;
  19. use Cake\Datasource\Paginator;
  20. use Cake\Datasource\QueryInterface;
  21. use Cake\Network\Exception\NotFoundException;
  22. use InvalidArgumentException;
  23. /**
  24. * This component is used to handle automatic model data pagination. The primary way to use this
  25. * component is to call the paginate() method. There is a convenience wrapper on Controller as well.
  26. *
  27. * ### Configuring pagination
  28. *
  29. * You configure pagination when calling paginate(). See that method for more details.
  30. *
  31. * @link https://book.cakephp.org/3.0/en/controllers/components/pagination.html
  32. */
  33. class PaginatorComponent extends Component
  34. {
  35. /**
  36. * Default pagination settings.
  37. *
  38. * When calling paginate() these settings will be merged with the configuration
  39. * you provide.
  40. *
  41. * - `maxLimit` - The maximum limit users can choose to view. Defaults to 100
  42. * - `limit` - The initial number of items per page. Defaults to 20.
  43. * - `page` - The starting page, defaults to 1.
  44. * - `whitelist` - A list of parameters users are allowed to set using request
  45. * parameters. Modifying this list will allow users to have more influence
  46. * over pagination, be careful with what you permit.
  47. *
  48. * @var array
  49. */
  50. protected $_defaultConfig = [
  51. 'page' => 1,
  52. 'limit' => 20,
  53. 'maxLimit' => 100,
  54. 'whitelist' => ['limit', 'sort', 'page', 'direction']
  55. ];
  56. /**
  57. * Datasource paginator instance.
  58. *
  59. * @var \Cake\Datasource\Paginator
  60. */
  61. protected $_paginator;
  62. /**
  63. * {@inheritDoc}
  64. */
  65. public function __construct(ComponentRegistry $registry, array $config = [])
  66. {
  67. if (isset($config['paginator'])) {
  68. if (!$config['paginator'] instanceof Paginator) {
  69. throw new InvalidArgumentException('Paginator must be an instance of ' . Paginator::class);
  70. }
  71. $this->_paginator = $config['paginator'];
  72. unset($config['paginator']);
  73. } else {
  74. $this->_paginator = new Paginator();
  75. }
  76. parent::__construct($registry, $config);
  77. }
  78. /**
  79. * Events supported by this component.
  80. *
  81. * @return array
  82. */
  83. public function implementedEvents()
  84. {
  85. return [];
  86. }
  87. /**
  88. * Handles automatic pagination of model records.
  89. *
  90. * ### Configuring pagination
  91. *
  92. * When calling `paginate()` you can use the $settings parameter to pass in pagination settings.
  93. * These settings are used to build the queries made and control other pagination settings.
  94. *
  95. * If your settings contain a key with the current table's alias. The data inside that key will be used.
  96. * Otherwise the top level configuration will be used.
  97. *
  98. * ```
  99. * $settings = [
  100. * 'limit' => 20,
  101. * 'maxLimit' => 100
  102. * ];
  103. * $results = $paginator->paginate($table, $settings);
  104. * ```
  105. *
  106. * The above settings will be used to paginate any Table. You can configure Table specific settings by
  107. * keying the settings with the Table alias.
  108. *
  109. * ```
  110. * $settings = [
  111. * 'Articles' => [
  112. * 'limit' => 20,
  113. * 'maxLimit' => 100
  114. * ],
  115. * 'Comments' => [ ... ]
  116. * ];
  117. * $results = $paginator->paginate($table, $settings);
  118. * ```
  119. *
  120. * This would allow you to have different pagination settings for `Articles` and `Comments` tables.
  121. *
  122. * ### Controlling sort fields
  123. *
  124. * By default CakePHP will automatically allow sorting on any column on the table object being
  125. * paginated. Often times you will want to allow sorting on either associated columns or calculated
  126. * fields. In these cases you will need to define a whitelist of all the columns you wish to allow
  127. * sorting on. You can define the whitelist in the `$settings` parameter:
  128. *
  129. * ```
  130. * $settings = [
  131. * 'Articles' => [
  132. * 'finder' => 'custom',
  133. * 'sortWhitelist' => ['title', 'author_id', 'comment_count'],
  134. * ]
  135. * ];
  136. * ```
  137. *
  138. * Passing an empty array as whitelist disallows sorting altogether.
  139. *
  140. * ### Paginating with custom finders
  141. *
  142. * You can paginate with any find type defined on your table using the `finder` option.
  143. *
  144. * ```
  145. * $settings = [
  146. * 'Articles' => [
  147. * 'finder' => 'popular'
  148. * ]
  149. * ];
  150. * $results = $paginator->paginate($table, $settings);
  151. * ```
  152. *
  153. * Would paginate using the `find('popular')` method.
  154. *
  155. * You can also pass an already created instance of a query to this method:
  156. *
  157. * ```
  158. * $query = $this->Articles->find('popular')->matching('Tags', function ($q) {
  159. * return $q->where(['name' => 'CakePHP'])
  160. * });
  161. * $results = $paginator->paginate($query);
  162. * ```
  163. *
  164. * ### Scoping Request parameters
  165. *
  166. * By using request parameter scopes you can paginate multiple queries in the same controller action:
  167. *
  168. * ```
  169. * $articles = $paginator->paginate($articlesQuery, ['scope' => 'articles']);
  170. * $tags = $paginator->paginate($tagsQuery, ['scope' => 'tags']);
  171. * ```
  172. *
  173. * Each of the above queries will use different query string parameter sets
  174. * for pagination data. An example URL paginating both results would be:
  175. *
  176. * ```
  177. * /dashboard?articles[page]=1&tags[page]=2
  178. * ```
  179. *
  180. * @param \Cake\Datasource\RepositoryInterface|\Cake\Datasource\QueryInterface $object The table or query to paginate.
  181. * @param array $settings The settings/configuration used for pagination.
  182. * @return \Cake\Datasource\ResultSetInterface Query results
  183. * @throws \Cake\Network\Exception\NotFoundException
  184. */
  185. public function paginate($object, array $settings = [])
  186. {
  187. $request = $this->_registry->getController()->request;
  188. try {
  189. $results = $this->_paginator->paginate(
  190. $object,
  191. $request->getQueryParams(),
  192. $settings
  193. );
  194. $this->_setPagingParams();
  195. } catch (PageOutOfBoundsException $e) {
  196. $this->_setPagingParams();
  197. throw new NotFoundException(null, null, $e);
  198. }
  199. return $results;
  200. }
  201. /**
  202. * Merges the various options that Pagination uses.
  203. * Pulls settings together from the following places:
  204. *
  205. * - General pagination settings
  206. * - Model specific settings.
  207. * - Request parameters
  208. *
  209. * The result of this method is the aggregate of all the option sets combined together. You can change
  210. * config value `whitelist` to modify which options/values can be set using request parameters.
  211. *
  212. * @param string $alias Model alias being paginated, if the general settings has a key with this value
  213. * that key's settings will be used for pagination instead of the general ones.
  214. * @param array $settings The settings to merge with the request data.
  215. * @return array Array of merged options.
  216. */
  217. public function mergeOptions($alias, $settings)
  218. {
  219. $request = $this->_registry->getController()->request;
  220. return $this->_paginator->mergeOptions(
  221. $request->getQueryParams(),
  222. $this->_paginator->getDefaults($alias, $settings)
  223. );
  224. }
  225. /**
  226. * Set paginator instance.
  227. *
  228. * @param \Cake\Datasource\Paginator $paginator Paginator instance.
  229. * @return self
  230. */
  231. public function setPaginator(Paginator $paginator)
  232. {
  233. $this->_paginator = $paginator;
  234. return $this;
  235. }
  236. /**
  237. * Get paginator instance.
  238. *
  239. * @return \Cake\Datasource\Paginator
  240. */
  241. public function getPaginator()
  242. {
  243. return $this->_paginator;
  244. }
  245. /**
  246. * Set paging params to request instance.
  247. *
  248. * @return void
  249. */
  250. protected function _setPagingParams()
  251. {
  252. $request = $this->_registry->getController()->request;
  253. $request->addParams([
  254. 'paging' => $this->_paginator->getPagingParams()
  255. + (array)$request->getParam('paging')
  256. ]);
  257. }
  258. /**
  259. * Proxy getting/setting config options to Paginator.
  260. *
  261. * @deprecated 3.5.0 use setConfig()/getConfig() instead.
  262. * @param string|array|null $key The key to get/set, or a complete array of configs.
  263. * @param mixed|null $value The value to set.
  264. * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
  265. * @return mixed Config value being read, or the object itself on write operations.
  266. */
  267. public function config($key = null, $value = null, $merge = true)
  268. {
  269. $return = $this->_paginator->config($key, $value, $merge);
  270. if ($return instanceof Paginator) {
  271. $return = $this;
  272. }
  273. return $return;
  274. }
  275. /**
  276. * Proxy setting config options to Paginator.
  277. *
  278. * @param string|array $key The key to set, or a complete array of configs.
  279. * @param mixed|null $value The value to set.
  280. * @param bool $merge Whether to recursively merge or overwrite existing config, defaults to true.
  281. * @return $this
  282. */
  283. public function setConfig($key, $value = null, $merge = true)
  284. {
  285. $this->_paginator->setConfig($key, $value, $merge);
  286. return $this;
  287. }
  288. /**
  289. * Proxy getting config options to Paginator.
  290. *
  291. * @param string|null $key The key to get or null for the whole config.
  292. * @param mixed $default The return value when the key does not exist.
  293. * @return mixed Config value being read.
  294. */
  295. public function getConfig($key = null, $default = null)
  296. {
  297. return $this->_paginator->getConfig($key, $default);
  298. }
  299. /**
  300. * Proxy setting config options to Paginator.
  301. *
  302. * @param string|array $key The key to set, or a complete array of configs.
  303. * @param mixed|null $value The value to set.
  304. * @return $this
  305. */
  306. public function configShallow($key, $value = null)
  307. {
  308. $this->_paginator->configShallow($key, $value = null);
  309. return $this;
  310. }
  311. /**
  312. * Proxy method calls to Paginator.
  313. *
  314. * @param string $method Method name.
  315. * @param array $args Method arguments.
  316. * @return mixed
  317. */
  318. public function __call($method, $args)
  319. {
  320. return call_user_func_array([$this->_paginator, $method], $args);
  321. }
  322. }