zen of coding

Paginate associated model's data in CakePHP

This is a pretty simple tip, but I’ve noticed some beginners are having a problem with this scenario.

We are in the Users Controller and need to paginate all Comments for a given User (i.e. User.id = 5), well and probably display the User info while we are at it…
For this example we keep in mind that User hasMany Comment and Comment belongsTo User.

First, we prepare our controller to handle Comment pagination:

class UsersController extends AppController
{
    var $name = 'Users';

    var $paginate = array('Comment'=>array('limit'=>5));
}

Yep, even though we are in the Users Controller, we will be paginating the Comment model. (Obviously you can add other options, but we’ll just add a simple ‘limit’ for this example).

So what’s left to do?
Tell our paginate() method that we’ll be paginating the Comment model and pass appropriate User.id in the array of conditions:

[source language=”php”]
function index() {
$this->set(‘comments’, $this->paginate($this->User->Comment, array(‘User.id’=>5)));
}
[/source]

Remember, that because Comment belongsTo User, cake will build a JOIN and pass the correct condition to retrieve all of the relevant comments for this User.

… and for a little extra bonus let’s throw in a simple view:

<?php echo $paginator->numbers(); ?>

<?php foreach($comments as $comment): ?>

<div>
    <?php echo $comment['Comment']['comment']; ?>
</div>

<?php endforeach; ?>
  • ha, simple, yet brilliant – nice post.

  • @Neil Crookes

    I wouldn’t go as far as brilliant, but thanks :)

  • Dave

    How would you work this in to having multiple paginaters for a single Model? Lets say you wanted to paginate a users comments and their submissions/blog posts, could you just add another line to the initial array?

  • @Dave

    Do you mean paginate two models in the same view?
    AFAIK, it’s not possible, unless you are doing AJAX-pagination.

  • Dave

    Tek, thanks for the fast response. I was actually hoping to paginate a single model but not in it’s native view and use it multiple times. For example, I have a study model, each study has multiple locations, each location has multiple notebook entries. I want to display all the locations for the study on the view study page, and then under each location paginate the notebook entries. So it is one model being paginated, multiple times. Do you know if this is possible? Or do I have to use ajax.

  • @Dave

    Because of the way the URL’s are constructed there is no way to specify (at least that I know of) which instance of the model has to be paginated.
    You might try this AJAX pagination technique, which is quite easy to setup and works very well:
    http://bakery.cakephp.org/articles/view/easy-ajax-pagination-using-jquery

  • Pingback: CakePHP Digest #11 - Food Metaphors | PseudoCoder.com()

  • Pingback: Paginate associated model’s data in CakePHP | Dailytuts.net - Daily tutorial for peoples()

  • But, if I want to paginate in Content controller all content that belongs to a specify category (Content and Category are in relation HABTM) how can i do this ?

  • @marius

    There are a few articles/blogs showing how to do HABTM pagination. I don’t have the link handy, but I’m sure you can google it pretty quick…

  • I’m honestly having a reeaaaal hard time finding some documentation using HABTM pagination.

    Sorry to post this here but I am desperate by now.

    This is what I was trying right now but still no go.

    Ex: Groups HABTM Users

    I want to paginate the Groups where a certain user belongs to.

    $this->paginate = array(
    ‘order’ => ‘Group.name ASC’,
    ‘contain’ => array(
    ‘User’ => array(
    ‘conditions’ => array(
    ‘User.id’ => $this->Session->read(‘User.id’)
    )
    )
    )
    );

  • @Rui Cruz

    Please see my other post about forcing a join by using the hasOne bind trick. You can apply it to pagination in the exactly same manner. Just don’t forget to include the ‘false’ param in your bindModel().

  • demophase

    Im a newbie and this articles is good.. i quote :
    $this->set( ‘comments’, $this->paginate($this->User->Comment, array(‘User.id’=>5) );

    but what if we want to make the User.id was passed by the url, ex. domain.com/comments/5. (In my case it would be domain.com/products/catalog/45, where 45 is the category_id. And, Category hasMany Products, Products belongsTo Category)
    i wrote this code on my Products (C) :

    function catalog( $user_id = null) {
    …..
    $this->set( ‘products’, $this->paginate( ‘Product’, array( ‘Product.category_id’ => $user_id ));
    ….
    }

    it seem it would be worked for the first page, but if we click another page, this aint worked… Btw, i used CakePHP1.2.5. It would be great if yu could give me a suggestion, thx before, Tek.

    • demophas3

      Well i simply answer it myself… after i seen another articles on your page, it simply put another setting on the paging view, in this case we sett the $paginator->options(). And of course, i found it completely on the Official CakePHP Docs, i missed that section..

      Luckly, the paging view was created with elements, so there’s no need to update many views. We put :
      < ?php $paginator->options( array(‘url’ => $this->passedArgs) ); ?&gt

      And that’s it. I hope that every newbie who faced the problem would find this as a solution. Or maybe there’s another suggestion… i really hoped it.

  • hmm….

    Well… it doesnt quite work anymore… any help please… I did this in my controller

    [code]
    var $paginate = array(‘Project’=>array(‘limit’=>8));

    function view($name = null) {
    if (!$name) {
    $this->Session->setFlash(__(‘Invalid tag’, true));
    $this->redirect(array(‘action’ => ‘index’));
    }

    $this->set(‘projects’, $this->paginate($this->Tag->Project, array(‘Tag.name’=>$name)));
    }
    [/code]

  • Thank you! it helps me

  • And if I am on Comments controller, and want to paginate User, how to do that?
    Basicly the inverted situation.

    Thanks.

  • teknoid

    @Rafael Santos

    Change the model names around? What’s the difference?

  • wow. very simple.
    10x a lot

  • Ian Nieves

    This doesn’t in the case where you have a User who has made no Comment(s). The pagination will only work completely correctly if it is guaranteed that ALL User have at least one Comment.

    Any solutions the the situation of there there are some users with no comments?

    Cheers,
    Ian

  • Ian Nieves

    basically what i want is a full join between user and comment.. i think.

  • teknoid

    @Ian Nieves

    There are multiple ways to do that. INNER JOIN will filter out records that you don’t have in the Comment.

    To get a join, you can temporary associate User to Comment with “hasOne” or, use the ‘joins’ key (see manual) to tell cake to JOIN your tables rather than doing multiple selects.

  • Marnie Nickelson

    I realize this post is old, but it was a huge help to me today- very clear and easy to understand. Thank you!

%d bloggers like this: