Pagination Component

By Andy Dawson (AD7six)
The pagination component. For instruction on use see: http://bakery.cakephp.org/articles/view/65

Component Class:

Download code <?php 
/**
 * Pagination Component, responsible for managing the DATA required for pagination.
 */
class PaginationComponent extends Object
{
    
// Configuration/Default variables
/**
 * Specify whether the component will use AJAX links if available.
 * Tests for the presence of the RequestHandler component and if present will generate
 * AJAX links. However, if the prototype library js has not been included, normal updates
 * will take place.
 *
 * @var boolean
 * @access public
 */
    
var $ajaxAutoDetect true;
/**
 * Specify the div to update if using ajax updates
 *
 * @var string
 * @access public
 */
    
var $ajaxDivUpdate "content";
/**
 * The id of a form ID to be submitted with all pagination ajax links.
 *
 * @var string
 * @access public
 */
    
var $ajaxFormId NULL;
/**
 * Specify link style
 *
 * @var string "html"/"ajax"
 * @access public
 */
    
var $style "html";
/**
 * Specify link parameter style
 *
 * @var string "get"/"pretty"
 * @access public
 */
    
var $paramStyle "get";
/**
 * Used only for pretty style urls - must match the 
 *
 * @var string "get"/"pretty"
 * @access public
 */
    
var $paramSeperator "/";
/**
 * Specify DEFAULT start page number
 *
 * @var integer
 * @access public
 */
    
var $page 1;
/**
 * Specify DEFAULT number of items to be displayed per page. Also used as the limit
 * for the subsequent SQL search.
 *
 * @var integer
 * @access public
 */
    
var $show 5;
/**
 * Specify DEFAULT sort column.
 *
 * @var string
 * @access public
 */
    
var $sortBy 'id';
/**
 * Specify DEFAULT sort direction.
 *
 * @var string
 * @access public
 */
    
var $direction 'ASC';
/**
 * Specify the maximum number of pages to be included in the list of pages. Should be an odd number, otherwise rounded down.
 *
 * @var integer
 * @access public
 */
    
var $maxPages 10;
/**
 * Options for results per page.
 *
 * @var array
 * @access public
 */
    
var $resultsPerPage = array(2,5,10,20,50,100,500);
/**
 * Show links to the first and last page, if the number of pages exceeds the maxPage count.
 *
 * @var boolean
 * @access public
 */
    
var $showLimits true;
/**
 * An array of parameter names which cannot be specified by the url
 *
 * @var array
 * @access public
 */
    
var $privateParams = Array();

    
// Do not edit below this line unless you wish to customize the core functionality of this Component
/**
 * Place holder for the sort class. Irrelavent for models without associations
 *
 * @var boolean
 * @access private
 */
    
var $sortByClass NULL;
/**
 * Place holder for the model class.
 *
 * @var boolean
 * @access private
 */
    
var $modelClass NULL;
/**
 * Place holder for the base url
 *
 * @var boolean
 * @access private
 */
    
var $url NULL;
/**
 * Place holder for the controller
 *
 * @var boolean
 * @access private
 */
    
var $controller true;
/**
 * Place holder for the sanitize object
 *
 * @var boolean
 * @access private
 */
    
var $sanitize true;
/**
 * Place holder for the data array passed to the view
 *
 * @var boolean
 * @access private
 */
    
var $paging;

/**
 * Startup - Link the component to the controller.
 *
 * @param controller
 */
    
function startup(&$controller)
    {
        
$this->controller =& $controller;        
    }
/**
 * Initialize the pagination data.
 *
 * @param unknown
 * @param array
 * @options array
 * @return array
 */
    
function init($criteria=NULL,$parameters=Array(),$options=Array())
    {
        
uses('sanitize');
        
$this->Sanitize = &new Sanitize;

        
$this->_initFields($options);
        
$this->_checkAjax();
        
$this->_initSort();
        
$this->_initPaging($parameters);
        
$this->_initURL();

        
$this->_setParameter("show",$parameters);
        
// If the number of results per page isn't in the list, reset to default
        
if ((isset($this->paging["show"]))&&(!in_array($this->paging["show"],$this->resultsPerPage)))
        {
            
$this->paging["show"]=$this->paging['Defaults']['show'];
        }
        
        
$this->_setParameter("page",$parameters);
        
$this->_setParameter("sortBy",$parameters);
        
$this->_setParameter("sortByClass",$parameters); // Overriding the model class if specified.
        
$this->_setParameter("direction",$parameters);

        
$this->_check4Form();

        
$this->_setPrivateParameter("ajaxDivUpdate");
        
$this->_setPrivateParameter("ajaxFormId");
        
$this->_setPrivateParameter("maxPages");
        
$this->_setPrivateParameter("showLimits");
        
$this->_setPrivateParameter("style");
        
$this->_setPrivateParameter("paramStyle");
        
$this->_setPrivateParameter("paramSeperator");
        
$this->_setPrivateParameter("url");

        if (isset(
$this->total)) // If the field is already set, we  passed in the options the total number of results
        
{
            
$count $this->total;
        }
        else
        {
            
$count $this->controller->{$this->modelClass}->findCount($criteria,0);    
        }
        
$this->checkPage($count);
        
$this->paging['total'] = $count;
        
$this->trimResultsPerPage($count);

        
$this->_setPrivateParameter("resultsPerPage");

        
$this->paging['pageCount'] = ceil($count $this->paging['show'] );

        
$this->controller->set('paging',$this->paging);
        
        
$this->order $this->paging['sortByClass'].".".$this->paging['sortBy'].' '.strtoupper($this->paging['direction']);
        
        
// For backwards compatability & clarity
        
$this->limit $this->paging['show'];
        
$this->page $this->paging['page'];
        
        
// For less code in the calling method..
        
return (Array($this->order,$this->paging['show'],$this->paging['page']));
    }
    
/**
 * Don't give the choice to display pages with no results
 *
 * @param integer
 */
    
function trimResultsPerPage ($count 0)
    {
        while ((
$limit current($this->resultsPerPage))&&(!isset($capKey))) 
        {
            if (
$limit >= $count
            {
                
$capKey key($this->resultsPerPage);
            }
            
next($this->resultsPerPage);
        
            if (isset(
$capKey))
            {
                
array_splice($this->resultsPerPage, ($capKey+1));
            }
        }
    }

/**
 * Set the page to the last if there would be no results, and to 1 if a negetive
 * page number is specified
 *
 * @param integer
 */
    
function checkPage ($count 0
    {
      if (((
$this->paging['page']-1)*$this->paging['show'])>=$count
      {
            
$this->paging['page'] = floor($count/$this->paging['show']+0.99);
      }
    }
    
/**
 * Set Object fields
 *
 * @param unknown
 */
    
function _initFields($options)
    {
        foreach(
$options as $option=>$val)
        {
            
$this->$option $val;
        }
    }
/**
 * Set Pagination with default Parameters
 *
 * @param unknown
 */
    
function _initPaging($parameters)
    {
        
$this->paging['importParams']=$parameters;
        
$this->paging['Defaults'] = Array (
                                        
"page"=>$this->page,
                                        
"show"=>$this->show,
                                        
"sortBy"=>$this->sortBy,
                                        
"sortByClass"=>$this->sortByClass,
                                        
"direction"=>$this->direction
                                            
);        
    }
/**
 * If everything is in place, use Ajax by default
 *
 * @param unknown
 */
    
function _checkAjax()
    {
        if ((
$this->ajaxAutoDetect==true)&&(isset($this->controller->RequestHandler)&&(in_array("Ajax",$this->controller->helpers))))
        {
            
$this->style "ajax";            
        }
    }

/**
 * Set the DEFAULT sort class
 *
 * @param unknown
 */
    
function _initSort()
    {
        if (!
$this->modelClass)
        {
            
$ModelClass $this->modelClass $this->controller->modelClass;
        }
        else
        {
            
$ModelClass $this->modelClass;
        }
        if (!
$this->sortBy)
        {
        
$this->sortBy $this->controller->$ModelClass->primaryKey;
        }
        if (!
$this->sortByClass)
        {
            
$this->sortByClass $ModelClass;
        }
    }

/**
 * Set the base url for updates.
 *
 * @param unknown
 */
    
function _initURL()
    {
        if (
$this->url// A url was specified in the paramters
        
{
            if (
substr($this->url, -11)<>"/")
            {
                
$this->url .= "/";
            }
        }
        else 
// No url in the parameters, derive it.
        
{
            if (
$this->paramStyle=="get")
            {
                
$this->url str_replace($this->controller->webroot,"/",$this->controller->here);
            }
            else
            {
                
$this->url "";
                if (isset(
$this->controller->params['admin']))
                {
                    
$this->url .= "/".$this->controller->params['admin'];
                    
$action substr($this->controller->actionstrlen($this->controller->params['admin']."_"));
                }
                else
                {
                    
$action $this->controller->action;
                }
                if (
$this->controller->plugin)
                {
                    
$this->url .= "/".$this->controller->plugin;
                }
                
$this->url .= "/".$this->controller->name;
                
$this->url .= "/".$action;
                if (isset(
$this->paging['importParams']['_unamedParameters']))
                {
                    
$unnamedString implode ("/"$this->paging['importParams']['_unamedParameters']);
                    
$this->url .= "/".$unnamedString;
                    unset(
$this->paging['importParams']['_unamedParameters']);
                }
                
$this->url .= "/";
            }
        }
        if (
defined('BASE_URL')) { // Hack for no mod_rewrite
            
$this->url preg_replace"!".BASE_URL."!"''$this->url); // Remove the base from the url
            
$this->url preg_replace("!\?.*!"''$this->url); // Remove the get parameters
        
}
    }

/**
 * If the parameters have been changed/set by a form action, update the params array.
 * Would perhaps be best to redirect to the equivalent url, which isn't implemented as
 * the relavent method is in the helper and as such inaccessible here.
 *
 * @param unknown
 */
    
function _check4Form()
    {
        if(isset(
$this->controller->data['pagination']))
        {
            if (isset(
$this->controller->data['pagination']['sortByComposite']))
            {
                
$Composite = Array();
                
$Composite explode("::",$this->controller->data['pagination']['sortByComposite']);
                if (isset(
$Composite[0]))
                {
                    
$this->controller->data['pagination']['sortBy'] = $Composite[0];    
                }
                if (isset(
$Composite[1]))
                {
                    
$this->controller->data['pagination']['direction'] = $Composite[1];    
                }
                if (isset(
$Composite[2]))
                {
                    
$this->controller->data['pagination']['sortByClass'] = $Composite[2];    
                }
                else
                {
                    
$this->controller->data['pagination']['sortByClass'] = $this->paging['Defaults']['sortByClass'];
                }
                unset(
$this->controller->data['pagination']['sortByComposite']);            
            }
            foreach(
$this->controller->data['pagination'] as $parameter=>$value)
            {
                
                if (!
in_array($parameter$this->privateParams))
                {
                    
$this->paging[$parameter] = $this->Sanitize->paranoid($value,array("-","_"));
                }                
            }
        }
    }

/**
 * Set a parameter to be passed to the view which cannot be specified/overriden from the url.
 *
 * @param unknown
 */
    
function _setPrivateParameter($parameter)
    {
        
$this->paging[$parameter]= $this->$parameter;
    }

/**
 * Set a parameter to be passed to the view overriden from the url if present.
 *
 * @param unknown
 * @param array
 * @param field
 */
    
function _setParameter($parameter,$parameters=Array(),$field=NULL)
    {
        
$field $field?$field:$parameter;

        if (
in_array($parameter$this->privateParams))
        {
            
$this->paging[$field] = $this->paging['Defaults'][$field];
        }
        else
        {
            if (
$this->paramStyle=="get")
            {
                if (isset(
$_GET[$parameter]))
                {
                    
$this->paging[$field] = $this->Sanitize->paranoid($_GET[$parameter],array("-","_"));        
                }
                else
                {
                    
$this->paging[$field]= $this->$field;
                }
            }
            elseif (
$this->paramStyle=="pretty")
            {
                if (isset(
$parameters[$parameter]))
                {
                    
$this->paging[$field] = $this->Sanitize->paranoid($parameters[$parameter],array("-","_"));        
                }
                else
                {
                    
$this->paging[$field]= $this->$field;
                }
            }
            else
            {
                echo (
"parameter error");
                die;
            }
        }
    }
}
?>

 

Comments 67

CakePHP Team Comments Author Comments
 

Bug

1 setParameter

Hi! Great work on the pagination component. I just found this bug though where if I have a sortBy parameter that has an underscore (e.g. 'first_name'), using Sanitize::paranoid() will remove the underscore, causing a SQL error when the query is executed. Instead, I think using Sanitize::cleanValue() should be a better option.
Posted Oct 16, 2006 by Evan Sagge
 

Comment

2 Underscores

Hi Evan,

Thanks for your comment, glad you found it useful.

In the code above the call to paranoid will not strip underscores or dashes, the previous version (on cakeforge, and the version on my site) didn't do that. cleanValue may be a better choice and if there is any need to update the code I will consider including that.
Posted Oct 16, 2006 by Andy Dawson
 

Bug

3 SortBy and Count

I believe that "$count = $this->controller->{$this->modelClass}->findCount($criteria,0); should be: "$count = $this->controller->{$this->sortByClass}->findCount($criteria,0);" in order to work properly in conjunction with model overrides.
Posted Apr 2, 2007 by Scott
 

Comment

4 Awesome component

Works like a charm. Thank you.
Posted Jul 15, 2007 by Tudor Laurentiu
 

Question

5 how to access this component

Please anybody help me to how to access this component....
Posted Jul 24, 2007 by Palanisamy E
 

Bug

6 Possibly a bug maybe I Messed sumthing up

First off great work!

Second, I added 2 methods to override the default sorting model and field, and made the update suggested in comment 3.

Everything works great EXCEPT,

The results per page (2,5,10....500) is set in my component.
However in my pages both the page navigation bar and the sorting drops downs only show 2,5,10.

How can I allow users to select from all the assigned values?! Where is that array being restricted or over written?
Posted Jun 7, 2008 by Eddie