PageRenderTime 54ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/en/core-libraries/components/pagination.rst

https://github.com/markstory/cakephp-docs
ReStructuredText | 341 lines | 262 code | 79 blank | 0 comment | 0 complexity | afdfe1de2e8fe7a308147b5e5e9f07c5 MD5 | raw file
  1. Pagination
  2. ##########
  3. .. php:class:: PaginatorComponent(ComponentCollection $collection, array $settings = array())
  4. One of the main obstacles of creating flexible and user-friendly
  5. web applications is designing an intuitive UI. Many applications
  6. tend to grow in size and complexity quickly, and designers and
  7. programmers alike find they are unable to cope with displaying
  8. hundreds or thousands of records. Refactoring takes time, and
  9. performance and user satisfaction can suffer.
  10. Displaying a reasonable number of records per page has always been
  11. a critical part of every application and used to cause many
  12. headaches for developers. CakePHP eases the burden on the developer
  13. by providing a quick, easy way to paginate data.
  14. Pagination in CakePHP is offered by a Component in the controller, to make
  15. building paginated queries easier. In the View :php:class:`PaginatorHelper` is
  16. used to make the generation of pagination links & buttons simple.
  17. Query Setup
  18. ===========
  19. In the controller, we start by defining the query conditions pagination will use
  20. by default in the ``$paginate`` controller variable. These conditions, serve as
  21. the basis of your pagination queries. They are augmented by the sort, direction
  22. limit, and page parameters passed in from the url. It is important to note
  23. here that the order key must be defined in an array structure like below::
  24. class PostsController extends AppController {
  25. public $components = array('Paginator');
  26. public $paginate = array(
  27. 'limit' => 25,
  28. 'order' => array(
  29. 'Post.title' => 'asc'
  30. )
  31. );
  32. }
  33. You can also include other :php:meth:`~Model::find()` options, such as
  34. ``fields``::
  35. class PostsController extends AppController {
  36. public $components = array('Paginator');
  37. public $paginate = array(
  38. 'fields' => array('Post.id', 'Post.created'),
  39. 'limit' => 25,
  40. 'order' => array(
  41. 'Post.title' => 'asc'
  42. )
  43. );
  44. }
  45. Other keys that can be included in the ``$paginate`` array are
  46. similar to the parameters of the ``Model->find('all')`` method, that
  47. is: ``conditions``, ``fields``, ``order``, ``limit``, ``page``, ``contain``,
  48. ``joins``, and ``recursive``. In addition to the aforementioned keys, any
  49. additional keys will also be passed directly to the model find methods. This
  50. makes it very simple to use behaviors like :php:class:`ContainableBehavior` with
  51. pagination::
  52. class RecipesController extends AppController {
  53. public $components = array('Paginator');
  54. public $paginate = array(
  55. 'limit' => 25,
  56. 'contain' => array('Article')
  57. );
  58. }
  59. In addition to defining general pagination values, you can define more than one
  60. set of pagination defaults in the controller, you just name the keys of the
  61. array after the model you wish to configure::
  62. class PostsController extends AppController {
  63. public $paginate = array(
  64. 'Post' => array (...),
  65. 'Author' => array (...)
  66. );
  67. }
  68. The values of the ``Post`` and ``Author`` keys could contain all the properties
  69. that a model/key less ``$paginate`` array could.
  70. Once the ``$paginate`` variable has been defined, we can use the
  71. :php:class:`PaginatorComponent`'s ``paginate()`` method from our controller
  72. action. This will return ``find()`` results from the model. It also sets some
  73. additional paging parameters, which are added to the request object. The
  74. additional information is set to ``$this->request->params['paging']``, and is
  75. used by :php:class:`PaginatorHelper` for creating links.
  76. :php:meth:`PaginatorComponent::paginate()` also adds
  77. :php:class:`PaginatorHelper` to the list of helpers in your controller, if it
  78. has not been added already.::
  79. public function list_recipes() {
  80. $this->Paginator->settings = $this->paginate;
  81. // similar to findAll(), but fetches paged results
  82. $data = $this->Paginator->paginate('Recipe');
  83. $this->set('data', $data);
  84. }
  85. You can filter the records by passing conditions as second
  86. parameter to the ``paginate()`` function.::
  87. $data = $this->Paginator->paginate('Recipe', array('Recipe.title LIKE' => 'a%'));
  88. Or you can also set ``conditions`` and other pagination settings array inside
  89. your action.::
  90. public function list_recipes() {
  91. $this->Paginator->settings = array(
  92. 'conditions' => array('Recipe.title LIKE' => 'a%'),
  93. 'limit' => 10
  94. );
  95. $data = $this->Paginator->paginate('Recipe');
  96. $this->set(compact('data'));
  97. );
  98. Custom Query Pagination
  99. =======================
  100. If you're not able to use the standard find options to create the query you need
  101. to display your data, there are a few options. You can use a
  102. :ref:`custom find type <model-custom-find>`. You can also implement the
  103. ``paginate()`` and ``paginateCount()`` methods on your model, or include them in
  104. a behavior attached to your model. Behaviors implementing ``paginate`` and/or
  105. ``paginateCount`` should implement the method signatures defined below with the
  106. normal additional first parameter of ``$model``::
  107. // paginate and paginateCount implemented on a behavior.
  108. public function paginate(Model $model, $conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
  109. // method content
  110. }
  111. public function paginateCount(Model $model, $conditions = null, $recursive = 0, $extra = array()) {
  112. // method body
  113. }
  114. It's seldom you'll need to implement paginate() and paginateCount(). You should
  115. make sure you can't achieve your goal with the core model methods, or a custom
  116. finder. To paginate with a custom find type, you should set the ``0``'th
  117. element, or the ``findType`` key as of 2.3::
  118. public $paginate = array(
  119. 'popular'
  120. );
  121. Since the 0th index is difficult to manage, in 2.3 the ``findType`` option was
  122. added::
  123. public $paginate = array(
  124. 'findType' => 'popular'
  125. );
  126. The ``paginate()`` method should implement the following method signature. To
  127. use your own method/logic override it in the model you wish to get the data
  128. from::
  129. /**
  130. * Overridden paginate method - group by week, away_team_id and home_team_id
  131. */
  132. public function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
  133. $recursive = -1;
  134. $group = $fields = array('week', 'away_team_id', 'home_team_id');
  135. return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group'));
  136. }
  137. You also need to override the core ``paginateCount()``, this method
  138. expects the same arguments as ``Model::find('count')``. The example
  139. below uses some Postgres-specifc features, so please adjust
  140. accordingly depending on what database you are using::
  141. /**
  142. * Overridden paginateCount method
  143. */
  144. public function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
  145. $sql = "SELECT DISTINCT ON(week, home_team_id, away_team_id) week, home_team_id, away_team_id FROM games";
  146. $this->recursive = $recursive;
  147. $results = $this->query($sql);
  148. return count($results);
  149. }
  150. The observant reader will have noticed that the paginate method
  151. we've defined wasn't actually necessary - All you have to do is add
  152. the keyword in controller's ``$paginate`` class variable::
  153. /**
  154. * Add GROUP BY clause
  155. */
  156. public $paginate = array(
  157. 'MyModel' => array(
  158. 'limit' => 20,
  159. 'order' => array('week' => 'desc'),
  160. 'group' => array('week', 'home_team_id', 'away_team_id')
  161. )
  162. );
  163. /**
  164. * Or on-the-fly from within the action
  165. */
  166. public function index() {
  167. $this->Paginator->settings = array(
  168. 'MyModel' => array(
  169. 'limit' => 20,
  170. 'order' => array('week' => 'desc'),
  171. 'group' => array('week', 'home_team_id', 'away_team_id')
  172. )
  173. );
  174. }
  175. In CakePHP 2.0, you no longer need to implement ``paginateCount()`` when using
  176. group clauses. The core ``find('count')`` will correctly count the total number
  177. of rows.
  178. Control which fields used for ordering
  179. ======================================
  180. By default sorting can be done with any column on a model. This is sometimes
  181. undesirable as it can allow users to sort on un-indexed columns, or virtual
  182. fields that can be expensive to calculate. You can use the 3rd parameter of
  183. ``PaginatorComponent::paginate()`` to restrict the columns sorting will be done on::
  184. $this->Paginator->paginate('Post', array(), array('title', 'slug'));
  185. This would allow sorting on the title and slug columns only. A user that sets
  186. sort to any other value will be ignored.
  187. Limit the maximum number of rows that can be fetched
  188. ====================================================
  189. The number of results that are fetched is exposed to the user as the
  190. ``limit`` parameter. It is generally undesirable to allow users to fetch all
  191. rows in a paginated set. By default CakePHP limits the maximum number of rows
  192. that can be fetched to 100. If this default is not appropriate for your
  193. application, you can adjust it as part of the pagination options::
  194. public $paginate = array(
  195. // other keys here.
  196. 'maxLimit' => 10
  197. );
  198. If the request's limit param is greater than this value, it will be reduced to
  199. the ``maxLimit`` value.
  200. .. _pagination-with-get:
  201. Pagination with GET parameters
  202. ==============================
  203. In previous versions of CakePHP you could only generate pagination links using
  204. named parameters. But if pages were requested with GET parameters they would
  205. still work. For 2.0, we decided to make how you generate pagination parameters
  206. more controlled and consistent. You can choose to use either querystring or
  207. named parameters in the component. Incoming requests will accept only the chosen
  208. type, and the :php:class:`PaginatorHelper` will generate links with the chosen type of
  209. parameter::
  210. public $paginate = array(
  211. 'paramType' => 'querystring'
  212. );
  213. The above would enable querystring parameter parsing and generation. You can
  214. also modify the ``$settings`` property on the PaginatorComponent::
  215. $this->Paginator->settings['paramType'] = 'querystring';
  216. By default all of the typical paging parameters will be converted into GET
  217. arguments.
  218. .. note::
  219. You can run into a situation where assigning a value to a nonexistent property will throw errors::
  220. $this->paginate['limit'] = 10;
  221. will throw the error Notice: Indirect modification of overloaded property $paginate has no effect.
  222. Assigning an initial value to the property solves the issue::
  223. $this->paginate = array();
  224. $this->paginate['limit'] = 10;
  225. //or
  226. $this->paginate = array('limit' => 10);
  227. Or just declare the property in the controller class::
  228. class PostsController {
  229. public $paginate = array();
  230. }
  231. Or use ``$this->Paginator->settings = array('limit' => 10);``
  232. Make sure you have added the Paginator component to your $components array if
  233. you want to modify the ``$settings`` property of the PaginatorComponent.
  234. Either of these approaches will solve the notice errors.
  235. Out of range page requests
  236. ==========================
  237. As of 2.3 the PaginatorComponent will throw a `NotFoundException` when trying to
  238. access a non-existent page, i.e. page number requested is greater than total
  239. page count.
  240. So you could either let the normal error page be rendered or use a try catch
  241. block and take appropriate action when a `NotFoundException` is caught::
  242. public function index() {
  243. try {
  244. $this->Paginator->paginate();
  245. } catch (NotFoundException $e) {
  246. //Do something here like redirecting to first or last page.
  247. //$this->request->params['paging'] will give you required info.
  248. }
  249. }
  250. AJAX Pagination
  251. ===============
  252. It's very easy to incorporate Ajax functionality into pagination.
  253. Using the :php:class:`JsHelper` and :php:class:`RequestHandlerComponent` you can
  254. easily add Ajax pagination to your application. See :ref:`ajax-pagination` for
  255. more information.
  256. Pagination in the view
  257. ======================
  258. Check the :php:class:`PaginatorHelper` documentation for how to create links for
  259. pagination navigation.
  260. .. meta::
  261. :title lang=en: Pagination
  262. :keywords lang=en: order array,query conditions,php class,web applications,headaches,obstacles,complexity,programmers,parameters,paginate,designers,cakephp,satisfaction,developers