Auto Paginate Component for CakePHP (Persisting Pagination Limits 2.0)

Nothing like writing a 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 and boost its portability by a billion percent. So, I whipped it up and called it AutoPaginate. Usage is much simpler; no more AppController silliness, just setting up the view files and individual controllers for pagination. I’ll paste in those parts of the tutorial from version 1.0. Speaking of which:
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 here’s a component that shows how easy it is to put in user-modifiable, session-persisting pagination limits.
The full component (app/controllers/components/auto_paginate.php):

<?php
/**
* Auto Paginate Component class.
*
* A simple extension for paginating that helps with persisting user-defined pagination limits.
*
* @filesource
* @author Jamie Nay
* @copyright Jamie Nay
* @license http://www.opensource.org/licenses/mit-license.php The MIT License
* @link http://jamienay.com/code/auto-paginate-component
*/
class AutoPaginateComponent extends Object {

/**
* Other components needed by this component
*
* @access public
* @var array
*/
public $components = array('Session');

/**
* component settings
*
* @access public
* @var array
*/
public $settings = array();

/**
* Default values for settings.
* - options: the results-per-page options to present to the user.
*
* @access private
* @var array
*/
private $__defaults = array(
'options' => array(1, 5, 10, 25, 50, 100),
'defaultLimit' => 25
);

/**
* Configuration method.
*
* @access public
* @param object $model
* @param array $settings
*/
public function initialize(&$controller, $settings = array()) {
$this->settings = array_merge($this->__defaults, $settings);
$this->controller =& $controller;
}

/**
* beforeRender()
*
* Set the variables needed by the controller.
*
* @access public
* @param $controller Controller object
*/
public function beforeRender(&$controller) {
$controller->set('paginationOptions', $this->settings['options']);
$controller->set('paginationLimit', $this->paginationLimit());
}

/**
*
* Set the controller's $paginate variable.
*
* @access public
* @param array $options
*/
public function setPaginate($options = array()) {
$defaults = array(
'limit' => $this->paginationLimit()
);

$this->controller->paginate = array_merge($defaults, $options);
}

/**
* Set the pagination limit based on user input and session variables.
*
* @access public
*/
public function paginationLimit() {
if (isset($this->controller->params['named']['Paginate'])) {
$this->Session->write('Pagination.limit', $this->controller->params['named']['Paginate']);
}

return ($this->Session->check('Pagination.limit') ? $this->Session->read('Pagination.limit') :
$this->settings['defaultLimit']);
}

}

?>

Two config options: options’, which determines the available results-per-page options for the user, and defaultLimit’, which, predictably, determines the default pagination results limit.
To use the component, just plug it into your AppController’s $components array (app/app_controller.php):

var $components = array('AutoPaginate');

Now we need to change the way we configure pagination in individual controllers. This is the easy part – just call AutoPaginate::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->AugoPaginate->setPaginate(array(
'contain' => array('Team', 'Position')
));

$this->set('players', $this->paginate());
}

Finally, since our pagination will be standardized through the entire site, we’ll set up a couple of common view elements.
The top 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[] = $html->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)
$paginationOptions and $paginationLimit are set in AutoPaginateComponent.
We’ll also make a new element for the bottom of paginated results too (app/views/elements/pagination/bottom.ctp):

<?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'));?>

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):

<?php __('Players');?>

<?php echo $this->element('pagination/top'); ?>

.... (data in table) ...
<?php echo $this->element('pagination/bottom'); ?>

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!