Integrating Pear Pager

By Heiner Gassen (kitten)
PEAR (http://pear.php.net) has a decent Pager class that is easy to integrate in Cake.

1 - Download and install

Download the PEAR pager package from http://pear.php.net/package/Pager. Unpack it and put the libraries in (e.g.) /vendors/Pear/Pager.


2 - Make a component

Make a component "pager.php". This will act as a wrapper between CakePHP and Pager. This way, we can do all the Pager setup outside of the controller. Most parameters will probably remain unchanged between the different invocations. We have the possibility to overwrite specific parameters later from the controller.

Component Class:

Download code <?php 
class PagerComponent extends Object
{    
    
/**
     * The (calling) controller object.
     *
     * @access public
     * @var object
     */
     
var $Controller;

     
/**
      * The pager object.
      *
      * @access public
      * @var object
      */
     
var $Pager;
     
     
/**
      * Configuration parameters
      *
      * @access public
      * @var array
      */
     
var $params;

    
/**
     * Component pseudo controller
     *
     * @access public
     * @param object $controller Calling controller object
     * @return void
     */
    
function startup(&$controller) {
        
$this->Controller =& $controller;
    }
    
    
/**
     * Initializes the pager. Must be called before using the component.
     *
     * Takes user configuration and creates pager object ($this->Pager)
     *
     * @access public
     * @param array $config Configuration options for Pager::factory() method
     * @see http://pear.php.net/manual/en/package.html.pager.factory.php
     * @return void
     */
    
function init($config)
    {        
        
// Get the correct URL, even with admin routes
        
$here = array();        
        if (
defined('CAKE_ADMIN') && !empty($this->Controller->params[CAKE_ADMIN])) {
            
$here[0] = $this->Controller->params[CAKE_ADMIN];
            
$here[2] = substr($this->Controller->params['action'], strlen($this->Controller->params[CAKE_ADMIN]) + 1);
        } else {
            
$here[2] = $this->Controller->params['action'];
        }
        
$here[1] = Inflector::underscore($this->Controller->params['controller']);
        
ksort($here);
        
$url implode('/'$here);
                
        
// Set up the default configuration vars
        
$this->params = array(
            
'mode' => 'Sliding',
            
'perPage' => 10,
            
'delta' => 5,
            
'totalItems' => '',
            
'httpMethod' => 'GET',
            
'currentPage' => 1,
            
'linkClass' => 'pager',
            
'altFirst' => 'First page',
            
'altPrev '=> 'Previous page',
            
'altNext' => 'Next page',
            
'altLast' => 'Last page',
            
'separator' => '',
            
'spacesBeforeSeparator' => 1,
            
'spacesAfterSeparator' => 1,
            
'useSessions' => false,
            
'firstPagePre'     => '',
            
'firstPagePost' => '',
            
'firstPageText' => '<img src="'.$this->Controller->base.'/img/first.gif" alt="">',
            
'lastPagePre' => '',
            
'lastPagePost' => '',
            
'lastPageText' => '<img src="'.$this->Controller->base.'/img/last.gif" alt="">',
            
'prevImg' => '<img src="'.$this->Controller->base.'/img/prev.gif" alt="">',
            
'nextImg' => '<img src="'.$this->Controller->base.'/img/next.gif" alt="">',
            
'altPage' => 'Page',
            
'clearIfVoid' => true,
            
'append' => false,
            
'path' => '',
            
'fileName' => $this->Controller->base DS $url DS '%d',
            
'urlVar' => '',
        );
        
        
vendor('Pear/Pager/Pager');
        
        
// Merge with user config
        
$this->params array_merge($this->params$config);        
 
        
// sanitize requested page number
        
if (!in_array($this->params['currentPage'], range(1ceil($this->params['totalItems'] / $this->params['perPage'])))) {
            
$this->params['currentPage'] = 1;
        }
        
$this->Pager =& Pager::factory($this->params);
        
        
// Set the template vars
        
$this->Controller->set('pageLinks',   $this->Pager->getLinks());
        
$this->Controller->set('currentPage'$this->params['currentPage']);
        
$this->Controller->set('isFirstPage'$this->Pager->isFirstPage());
        
$this->Controller->set('isLastPage',  $this->Pager->isLastPage());
    }    
?>

3 - More Features

You should add other template vars as needed. Those you see are just the most often needed (by me).


4 - Load the component

Don't forget to load the component in your application! Set

PHP Snippet:

Download code <?php 
<?php var $components = array('Pager'); ?>
?>
in your controller.


5 - Controller setup


In your posts_controller.php, do the following:

Controller Class:

Download code <?php 
function index($page 1) { 
                 
        
// setup the pager 
        
$params = array( 
            
'perPage'     => 10
            
'totalItems'  => $this->Post->findCount(), 
            
'currentPage' => $page
        ); 
        
$this->Pager->init($params); 
                 
        
// get the data 
        
$this->set('data'$this->Post->findAll(nullnull'Post.created DESC'$this->Pager->params['perPage'], $this->Pager->params['currentPage'])); 
    } 
?>

I have chosen the more efficient of the two methods available to feed data to pager. Instead of selecting all records of a table and let Pager decide which records to show (which can be very memory intensive with large result sets), we give Pager the total number of items

PHP Snippet:

Download code <?php 
$this
->Post->findCount();
?>

and fetch only the rows we need for our page to display:

PHP Snippet:

Download code <?php 
$this
->Post->findAll(nullnull'Post.created DESC'$this->Pager->params['perPage'], $this->Pager->params['currentPage'];
?>

6 - In the view

You will output the pager (if appropriate) in your index.thtml as follows:

View Template:

Download code
<?php
// Display pager if there are pages to display
if ($pageLinks['all']) {
    echo 
'<div id="pager" class="pager">Pages:&nbsp;&nbsp;' $pageLinks['all'] . '</div>';
}
?>


7 - The End


That's all, folks :)

 

Comments 45

CakePHP Team Comments Author Comments
 

Comment

1 Navigation links

For more accessibility, "back/next/first/last" navigation links are easy to output with PEAR::Pager

I added a function in the component:

Component Class:

<?php 
function getNavLinks()
    {
        return 
$this->Pager->linkTags;
    }
?>

In the controller:

Controller Class:

<?php 
$this
->set('navLinks'$this->Pager->getNavLinks());
?>

And in the header of my default layout:

View Template:


<?php if(!empty($navLinks)) echo $navLinks;?>
Posted Oct 2, 2006 by Spout
 

Comment

2 Its nice example but it isnt work when you use admin.

I test this source but it isnt work good when you use admin.

There is problem:
...
'path' => '',
'fileName' => $this->Controller->base.DS.$this->Controller->params['controller'].DS.$this->Controller->params['action'].'/%d',
'urlVar' => '',
);

Because:
$this->Controller->params['controller'] return admin_name-actin, this generate bad url for pagination url.
Posted Oct 3, 2006 by Petr Vytlacil
 

Comment

3 Component enhanced

$this->Controller->params['controller'] return admin_name-action, this generate bad url for pagination url.
You are right, thank you for reporting. I added a patch to make it work with Cake admin routes.
Posted Oct 4, 2006 by Heiner Gassen
 

Bug

4 Error if admin route is undefined

If you leave CAKE_ADMIN uncommented in core.php, then this component issues a notice:


Notice: Use of undefined constant CAKE_ADMIN - assumed 'CAKE_ADMIN' in /app/controllers/components/pager.php

So, I fixed the init() function with one little addition. Instead of...

Component Class:

<?php 
if (!empty($this->Controller->params[CAKE_ADMIN])) {
?>

I check to see if the constant has been defined:

Component Class:

<?php 
if (defined('CAKE_ADMIN') and !empty($this->Controller->params[CAKE_ADMIN])) {
?>

Otherwise, great work! I appreciate this contribution very much.
Posted Oct 18, 2006 by Joel Stein
 

Comment

5 Fixed


Component Class:

<?php 
if (defined('CAKE_ADMIN') and !empty($this->Controller->params[CAKE_ADMIN])) {
?>
Thank you for this fix, I integrated it in the code :)
Posted Oct 20, 2006 by Heiner Gassen
 

Comment

6 Keep GET variables

$url = implode('/', $here);

// Keep variables defined in GET , except for 'url'
$args = isset( $this->Controller->params['url'] ) ? $this->Controller->params['url'] : '';

$get = '';
foreach($args as $name => $value ) {
if($name != 'url' ) $get .= $name . '=' . $value;
}
if( $get!='') $get = '?'.$get;
// .....
// ....
// Add to fileName parameter
'fileName' => $this->Controller->base . '/' . $url . '/' . '%d' . '/' . $get,
Posted Oct 22, 2006 by Oleg Sverdlov
 

Question

7 Paging urls out of wack

Hey there, first off thanks for this and second(And this is more than likely something I have done wrong) When the paged result set is displayed my links that come up(1 2 3 4 5) have a weird link format(http://localhost/blog/index.php%5Cposts/index%5C4)

What am I doing wrong?

Thanks
Posted Oct 1, 2007 by Lindsay
 

Comment

8 Not to worry I found it

Hey there, first off thanks for this and second(And this is more than likely something I have done wrong) When the paged result set is displayed my links that come up(1 2 3 4 5) have a weird link format(http://localhost/blog/index.php%5Cposts/index%5C4)

What am I doing wrong?

Thanks
I changed

'fileName' => $this->Controller->base . DS . $url . DS . '%d',

to

'fileName' => $this->Controller->base . "/" . $url . "/". '%d',

and it is working now
Posted Oct 1, 2007 by Lindsay
 

Question

9 How can paginate multiple columns

Hello, I just implemented the Pager and Pager_Wrapper in order to paginate a database results, but how can I display results lets say in two columns?

example:

item a | price a
-----------------
aa1 | 5.99
aa2 | 1.99
aa3 | 0.99

Thanks in advance.
Aldo
Posted Jul 14, 2008 by Aldo Zavala
 

Question

10 How can paginate multiple columns

Hello, I just implemented the Pager and Pager_Wrapper in order to paginate a database results, but how can I display results lets say in two columns?

example:

item a | price a
-----------------
aa1 | 5.99
aa2 | 1.99
aa3 | 0.99

Thanks in advance.
Aldo

Excuse me, I was trying to say a table like this:
--------------------------------------
| item a | price a | item a | price a |
--------------------------------------
| aa1 | 5.99 | aa2 | 4.99 |
| aa3 | 3.99 | aa4 | 1.50 |
| aa5 | 5.99 | aa6 | 1.99 |
---------------------------------------
Posted Jul 14, 2008 by Aldo Zavala