If you find yourself paginating a lot of results, you may want to give your users the ability to change the number of results per page they see. Easy enough, I know, but the user’s preference should also persist through the session no matter what data the application is serving up. And, ideally, we don’t want to POST every time we want to change the pagination limit: I like having it right in the URL for easy reading.
So, I’m just going to do a little quickie here to show you how easy it is to put in user-modifiable pagination limits. We’ll write a common view element for pagination, then we’ll turn to AppController and write a couple of functions to handle setting the pagination limit and interpreting changes to that limit. Finally, we’ll slightly modify the way we set up pagination in individual controllers to handle the new code. It’ll go something like this:
OK, so if all of our paginated results are going to look the same then we might as well have a common view element. This element will present the choices for the number of results per page, as well as the usual “display X out of X results” line. It looks like this (app/views/elements/pagination/top.ctp):
Results per page:
<?php
$results = array();
foreach ((array)$paginationOptions as $option) {
if ($paginationLimit == $option) {
$results[] = $option;
} else {
$args = $this->passedArgs;
$args['Paginate'] = $option;
$results[] = $htmla->link($option, $args);
}
}
echo implode(" | ", $results);
?>
<?php
echo $paginator->counter(array(
'format' => __('Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%.', true)
));
?>
That’ll produce a string of plain text links – not a form – that the user can use to change pagination limits. The output looks like this:
Results per page: 1 | 5 | 10 | 25 | 50 | 100 Page 1 of 105, showing 10 records out of 1042 total... (etc)
There are two variables in the pagination/top.ctp that we need to define, as well – $paginationOptions and $paginationLimit. You just need to set these somewhere in your AppController before the view is rendered, so in, say, beforeFilter() – I actually set mine in conjunction with my quick and dirty widget component (/end shameless plug
):
// Pagination options
$this->set('paginationOptions', array(1, 5, 10, 25, 50, 100));
$this->set('paginationLimit', $this->_paginationLimit());
We’ll write the _paginationLimit() function in the next section.
We’ll also make a new element for the bottom of paginated results too (app/views/elements/pagination/bottom.ctp):
<div class="paging">
<?php echo $paginator->prev('<< '.__('previous', true), array('url' => $this->passedArgs), null, array('class'=>'disabled'));?>
| <?php echo $paginator->numbers(array('url' => $this->passedArgs));?>
<?php echo $paginator->next(__('next', true).' >>', array('url' => $this->passedArgs), null, array('class'=>'disabled'));?></div>
Now to insert the elements into a typical view with typical paginated results. I like to insert top.ctp as a table caption, but you can do anything you’d like. If we’re paginating in, say, PlayersController::admin_index, it might look something like this (app/views/players/admin_index.ctp):
<h2><?php __('Players');?></h2>
<table cellpadding="0" cellspacing="0" class="table_list">
<caption>
<?php echo $this->element('pagination/top'); ?>
</caption>
.... (data in table) ...</table>
<?php echo $this->element('pagination/bottom'); ?>
The views are finished – now we just need the methods to make it all work.
Two new functions here, one that we’ll use in the individual controllers in place of defining $this->paginate, and another one called by that function to set the pagination limit depending on possible user input and the existing session-written value.
The first method, _setPaginate(), is just a shortcut for setting $this->paginate in controllers, so we don’t need to set the limit every time. It’s short and sweet (app/app_controller.php):
protected function _setPaginate($options = array()) {
$defaults = array(
'limit' => $this->_paginationLimit()
);
$this->paginate = array_merge($defaults, $options);
}
Notice that _setPaginate() calls _paginationLimit(), which is the function that sets and gets the limit in the user’s session:
protected function _paginationLimit() {
if (isset($this->params['named']['Paginate'])) {
$this->Session->write('Pagination.limit', $this->params['named']['Paginate']);
}
// On two lines for blog readability.
return ($this->Session->check('Pagination.limit') ? $this->Session->read('Pagination.limit') :
Configure::read('SiteSettings.default_pagination_limit'));
}
The system is done – now we just have to use it!
This is the easy part – just call _setPaginate() instead of explicitly defining $this->paginate when setting up your pagination. For example, in our PlayersController::admin_index() function we’re used to doing this:
$this->paginate = array(
'limit' => 20,
'contain' => array('Player, 'Position')
);
$this->set('players', $this->paginate());
But now we’ll just do this (app/controllers/players_controller.php):
function admin_index() {
$this->_setPaginate(array(
'contain' => array('Team', 'Position')
));
$this->set('players', $this->paginate());
}
Pretty simple, eh? I prefer using helper functions to set variables anyway, so this kills two birds in one stone by automatically setting the pagination limit and getting rid of the “$this->paginate =” business in controllers.
As always, comments, suggestions, improvements and helpful criticisms welcome!
3 Responses to Persisting Pagination Limits in CakePHP
Jamie Nay » Auto Paginate Component for CakePHP (Persisting Pagination Limits 2.0)
July 13th, 2009 at 7:54 am
[...] tutorial on your code to realize how you could improve it! Just minutes after writing a tutorial on persisting pagination limits in CakePHP – a system I developed weeks ago, I wondered to myself why I just didn’t make it a component [...]
Jamie Nay - Auto Paginate Component
July 17th, 2009 at 5:57 pm
[...] tutorial on your code to realize how you could improve it! Just minutes after writing a tutorial on persisting pagination limits in CakePHP – a system I developed weeks ago, I wondered to myself why I just didn’t make it a component [...]
adnan
January 25th, 2010 at 3:08 am
Here is another method for advance cakephp pagination with sort by derived field:
http://abcoder.com/php/cakephp/cakephp-advanced-pagination-sort-by-derived-field/
Hope it’ll be helpful for your development.
Thanks