Basic Pagination Overview (1.2)

This article is also available in the following languages:
By rtconner
Just a basic introduction to the wonderful abilities of pagination in Cake 1.2. Gives an overview of how to use pagination to do some very complex things with very little code.
Pagination in Cake 1.2 is very simple to set up and use. There is not much to it. I'll just walk you through step by step getting a basic table with pagination going. From there its on you to explore the more advanced options that exist.

A Quick Walk Through

For this example we'll use a Customer Model. So we'll be displaying a list of customers and sorting them and paging through them.

You do not need to do anything special in your Model

Model Class:

<?php 
class Customer extends AppModel {
  
    var 
$name "Customer";
}
?>

In your controller just add the $paginate variable. It tells the system to start and page 1, and show 15 entries per page. You will call the paginate function from the controller to generate the data list.

Controller Class:

<?php 
class CustomersController extends AppController {

    var 
$name 'Customers';

    var 
$paginate = array('limit' => 15'page' => 1);

    function 
display() {
        
$this->set('customers'$this->paginate('Customer'));
    }
}
?>

And lastly in your view you will wants to use the $paginator to display your data. You view will look something like this. This will be the file /views/customers/display.ctp.

View Template:

Showing Page <?php echo $paginator->counter(); ?>
<table>
    <tr>
        <th><?php echo $paginator->sort('Customer Name''name');?></th>
        <th><?php echo $paginator->sort('Store Location''store');?></th>
    </tr>
<?php foreach($customers as $customer): ?>
    <tr>
        <td><?php echo $customer['Customer']['name']; ?></td>
        <td><?php echo $customer['Customer']['store']; ?></td>
    </tr>
<?php endforeach; ?>
</table>
<?php echo $paginator->prev(); ?>
<?php 
echo $paginator->numbers(); ?>
<?php 
echo $paginator->next(); ?>


Yes, it is that simple. You might say that was "Cake".

Further Reading

There are two parts to pagination with CakePHP. The paginate function within the controller and the $paginator helper within the view. All of the options for these can be found in the 1.2 API documentation.

The paginate function is used to define your data set. You can pass in parameters to screen out certain data, perhaps only display customers from a certain store. This function is assisted by the $paginate variable in the controller which has a few options such as rows per page and default sort order.

The paginator helper does a great job of supporting Ajax pagination. Information is available in the API, but more information should come in a later tutorial on advanced pagination abilities. For now, the only real good tip I have is that you have to always use arrays when passing in custom urls.

You paginator Ajax links might look like this:
<?php echo 
$paginator->sort('Article Name''name', array('url'=>
            array(
'controller'=>'Articles''action'=>'index'),
            
'update'=>'ArticleListTable')); ?>
But like I said there is a lot to this. Ajax and Url handling will need a tutorial of its own.

Tips & Hints

To set a default sort order use the $paginate variable like this:
// in your Controller
var $paginate = array('order'=>array('name' => 'desc'));

You can use images for your prev and next links. You have to use the escape option.
<?php echo $paginator->link('<img src="myimage.jpg">', array('escape'=>false))?>
It is possible to have more than one paginator within a controller and view. You just have to specify the Model that you are going to use everywhere.
// this goes in the Controller
var $paginate = array('Article' => array('limit'=>25), 'Customer'=>array('limit'=>10));

<!-- these will go in the view -->
<?php echo $paginator->sort('Article Sort''id', array('model'=>'Article')); ?>
<?php 
echo $paginator->sort('Customer Name''name', array('model'=>'Customer')); ?>

The counter function of the $paginator helper is very powerful, giving you access to all kinds of data that you might want to display. Thanks to Gwoo for this lovely little piece of code.
<?php 
echo $paginator->counter(array(
        
'format' => 'Page %page% of %pages%, showing %current% records out of %count% total, starting on record %start%, ending on %end%'
)); 
?>

Comments

  • Posted 04/23/10 09:43:59 PM
    i have table name agenda... and in my agenda controller

    var $paginate = array('conditions' => array('Agenda.date >' => '2010-4-24'),'limit' => 6, 'page' => 1, 'order' => array('Agenda.id' => 'desc'));

    but i want the date 2010-4-24 become date(Y-m-d) so if the agenda.date < today date, the data doesn't show .... please help me :)
  • Posted 01/06/10 05:08:08 AM
    HI,
    I am writing following query for searching purpose.

    $this->paginate('Image', array('Image.keywords like'=>'%'.$_POST['keywords'].'%','Image.imageright in(\''.$_POST['imagerightne'].'\',\''.$_POST['imagerightex'].'\')'));

    I am getting result also but when i click on next page in pagination(or a particular page number) then I am getting next page with no CSS and no data and following error:

    Undefined variable: paginator

    Call to a member function counter() on a non-object in xyz.ctp file

    Please provide me the solutions.
  • Posted 12/30/09 07:56:51 AM
    Does anyone knows how to do something like this using paginate function :

    limit 0,5

    Thanks in advance.....
  • Posted 10/26/09 03:02:35 PM
    Thanks for the article. Very easy to follow. I was stumped on a few things but overall I learned a lot.
  • Posted 08/05/09 11:42:23 PM
    thank you this tutorial is very helpful :D
  • Posted 12/27/08 02:10:52 PM
    Hi,

    I wanna know if is possible to do the mysql LIKE with this helper.

    Example:
    SELECT * FROM nome_tbl WHERE key_col LIKE "Patrick%";

    How can i do this?

    Thanks por any help!
    • Posted 12/11/09 12:01:55 AM
      Hi,

      I wanna know if is possible to do the mysql LIKE with this helper.

      Example:
      SELECT * FROM nome_tbl WHERE key_col LIKE "Patrick%";

      How can i do this?

      Thanks por any help!

      try this:-

      $this->Controller->findAll('field like'=>$variable.'%');
  • Posted 12/23/08 08:03:34 AM
    Hi,

    First of all thanks for this nice tutorial.
    I am trying to sorting on HABTM association but not getting success.

    Tables:
    1) problems(id, title)
    2) users(id, username)
    3) problems_users(problem_id, user_id)

    Relation ship between problems and users table is HABTM

    I am successfully able to sort on problem title column using
    sort('Title', 'Problem.title'); ?>
    but when I am trying to sort on username using
    First :
    sort('Username', 'User.username', array('modal' => 'User')) ?> its generating errors:
    Warning (512): SQL Error: 1054: Unknown column 'User.username'

    Second :
    sort('Username', 'username', array('modal' => 'User')) ?> its not giving any error but not sorting data:

    Please tell me how can I sort a column (username) based on above HABTM associations?
    Thanks!!
  • Posted 07/29/08 11:50:43 AM
    hello, i've been trying to use images for the prev and next links but no success . I tried your code but it doesnt work. I get a regular link with the actual image tag text. Please help ! thanks
    • Posted 08/08/08 04:55:31 AM
      hello, i've been trying to use images for the prev and next links but no success . I tried your code but it doesnt work. I get a regular link with the actual image tag text. Please help ! thanks
      you have to use something like this:

      <?php echo $paginator->next($html->image('design/next.gif', array('border'=>'0')), array('escape'=>false));?>

      where design can be replaced with whateverfolder you are having in the design or not to be placed at all if you save your images in img folder.
  • Posted 04/30/08 02:30:03 AM
    If anyone would like to try dynamic pagination try this

    Controller Class:

    <?php 
    $conditions 
    array_merge($conditions, array('Manufacturer.name' => $this->params['named']['brand']));
    $this->paginate array_merge($this->paginate, array('conditions' => $conditions));
    ?>

    Model Class:

    <?php 
    echo $paginator->sort('Price''price', array('url' => array('brand:Sony')));
    ?>
  • Posted 03/19/08 05:01:04 PM
    I find it very annoying that there is no indicator of which column is being sorted on. I was able to hack in a few lines into the paginator helper that just add html ↑ and ↓ into the title cells. It is a little more painful than I'd like because I have to fight with an htmlspecialchars() call lurking in link(). And it probably should be driven off some $option value passed in to sort(). Anyway, is this something others would like to see? If so, where is the best place to submit patches/suggestions?
  • Posted 01/29/08 11:58:58 PM
    I have this scenario where I have two tables, 'users' and 'articles' table. Each user has many articles and I need to retrieve all users together with submitted article count and be able to paginate the result.

    I tried this using paginate method as given in the tutorial but I don't know where to put the GROUP BY clause within this method.

    What I can insert is the count articles inside the 'fields' array of paginate as shown below:

    $this->paginate['Article'] = array('fields' => array('SUM(IF(Article.status=\'Approved\',1,0)) as count'));

    $this->set('authors', $this->paginate('Article'));


    But this statement has an SQL Error since it is missing the GROUP By clause. What I also need to come-up is the query similar to the statement below and be able to paginate it:

    SELECT
    User.id,
    User.first_name,
    User.last_name,
    SUM(IF(Article.status='Approved',1,0)) as count
    FROM
    articles as Article
    LEFT JOIN users as User on Article.user_id = User.id
    GROUP BY
    Article.user_id ORDER BY count DESC




    Any comments, suggestions and or good solution is highly appreciated.


    Thank you very much for helping!

  • Posted 01/29/08 03:38:02 PM
    I had a special scenario where I needed to bind models during the pagination process. Fortunately, a custom paginate() and paginateCount() function can be specified in your model class. If you need to bind or unbind (or really perform any other special processing) prior to your pagination call, just add something like the following to your model.

    Model Class:

    <?php 
    function paginateCount($conditions null$recursive null) {
        
    $this->bindModel(array(
            
    'hasOne' => array(
                
    'Gender' => array(
                    
    'className'  => 'Gender',
                    
    'foreignKey' => 'profile_id',
                    
    'conditions' => 'Gender.gender_id = '.$conditions['Gender.id'],
                ),
            ),
        ));
        return 
    $this->findCount($conditions$recursive);
    }

    function 
    paginate($conditions null$fields null$order null$limit null$page 1$recursive null) {
        
    $this->bindModel(array(
            
    'hasOne' => array(
                
    'Gender' => array(
                    
    'className'  => 'Gender',
                    
    'foreignKey' => 'profile_id',
                    
    'conditions' => 'Gender.gender_id = '.$conditions['Gender.id'],
                ),
            ),
        ));
        return 
    $this->findAll($conditions$fields$order$limit$page$recursive);
    }
    ?>
  • Posted 01/29/08 03:33:09 PM
    Extremely helpful lesson, thanks a bunch!
  • Posted 01/08/08 05:43:15 AM
    I've been getting into cake over the last couple of weeks and wanted to add pagination to a project of mine. I've followed this guide on 1.2 but I get an undefined method calling paginate() from the controller.

    I've looked through the API but couldn't find any reference to paginate. I also checked if it's a helper I should be including.

    I'm stumped on this, and it looked so easy :P Any ideas?
    • Posted 01/08/08 05:55:03 AM
      I'm stumped on this, and it looked so easy :P Any ideas?
      Sorry forget this. I was using 1.1, apologies for dragging this post up
  • Posted 10/06/07 11:02:53 PM
    I'd also like to know how you can limit the results...paginate() seems to just be a blind findAll() -- it's simply pulling WAY too much data, especially with associated models.

    Anyway to limit the fields? Also maybe the recursiveness?

    ...well I tried $this->paginate['fields'] = array('Model.column', 'Model.column'); and it worked... So there's how you limit fields, but that's just for the model at hand.

    Still trying to figure how to limit the associated model fields...you can't put in Model1.column, Model2.column in that array for whatever reason.

    Though adding $this->Model->recursive = -1; before the $this->paginate['fields'] does do the trick...I'd rather not make several find calls after paginate. Guess it's better than having too much info though.
  • Posted 08/30/07 01:33:59 PM
    Hi. I had a question, now answered, that I will ask and answer here. (Actually, two, related, questions)

    1) How do you apply additional filters to pagination?
    - The pagination seems to only work on all members of a model.

    2) How do you maintain the URL params in the "next" and "prev" links?
    - The "page:X" and "sort:XXX" params seem to override the controller method params from the URL.

    Answers
    1) Pass in an array of "WHERE" (as in "SQL WHERE") conditions, for example (using this tuts model):

    $this->set('customers', $this->paginate('Customer', array("`Customer`.`id` in (1,2,3,4,5)));

    2) Pass in an option "url" as an array into the next(), prev(), and numbers() helper functions:

    e.g. prev(array('url'=>'S/J')); ?>
    (an example assuming that the display() function filters on first initial of last name/first name or something like that)

    I hope this is helpful, and, more importantly, correct!
    • Posted 04/24/08 12:59:40 AM
      2) Pass in an option "url" as an array into the next(), prev(), and numbers() helper functions:

      e.g. prev(array('url'=>'S/J')); ?>
      (an example assuming that the display() function filters on first initial of last name/first name or something like that)

      $paginator->prev() now accepts the first field as being a string text, so you'll have to do lit like so:

      <?php echo $paginator->prev('<< Previous', array('url'=> ... ?>
  • Posted 06/27/07 01:57:14 PM
    I have setup several HABTM but when i use paginate i get a huge number of queries, is it possible to define custom fields (columns) in paginate?
    • Posted 10/25/07 12:04:04 AM
      I have setup several HABTM but when i use paginate i get a huge number of queries, is it possible to define custom fields (columns) in paginate?
      it's very easy, in controller:

      var $paginate = array('fields'=>array('Customer.name', 'Customer.store'));
  • Posted 06/20/07 05:02:26 PM
    Thank you for posting this very helpful :-). My only gripe is that when the "<< Previous" text is shown the link is being underlined between it and the 1st page text, there should be a gap.
  • Posted 06/17/07 10:48:38 AM
    It works great except when i use another model.. for example when i have in my article controller:

    var $uses = array('Tag');

    Then i get this error:

    Notice (8): Undefined variable: paginator [CORE\app\views\articles\index.ctp, line 36]

    Fatal error: Call to a member function prev() on a non-object in C:\wamp\www\mech7\app\views\articles\index.ctp on line 36
    • Posted 06/18/07 10:36:08 AM
      See the Tips section on using multiple Models. If you are using a Model besides the default for a controller, you have to specify the name of the Model in both the paginate variable, function, and the $paginator functions.
  • Posted 06/13/07 09:57:26 AM
    PaginatorHelper only supports arrays for building URLs. This is by design.
    • Posted 06/13/07 12:31:58 PM
      PaginatorHelper only supports arrays for building URLs. This is by design. Aha. Thanks.. I wasn't aware it was by design. I'll modify that line to correct that information.

Comments are closed for articles over a year old