Laravel 4.1: How to paginate eloquent eager relationship?

To clarify something: paginating eager-loaded relationships is somewhat of a misconception. The point of eager loading is to retrieve all relationships in as few queries as you can. If you want to retrieve 10 topics, all of which have 35 posts, you will only need two queries. Sweet!

That said, paginating an eager-loaded relationship is not going to work. Consider two scenarios when eager loading happens:

  1. You want to retrieve and list topics, and maybe list the first five posts for each topic. Great! Eager loading is perfect. Now, you wouldn’t want to paginate the eager-loaded posts on a page like this, so it doesn’t matter.

  2. You load a single topic, and you want to paginate the posts for that topic. Great! Relatively easy to do. However, if you’ve already eager-loaded all posts belonging to this topic, you’ve just potentially retrieved a lot of extra resources that you don’t need. Therefore eager loading is actually hurting you.

That said, there are two potential solutions:

Option 1: Create a custom accessor that paginates the Eloquent relationship.

/**
 * Paginated posts accessor. Access via $topic->posts_paginated
 * 
 * @return \Illuminate\Pagination\Paginator
 */
public function getPostsPaginatedAttribute()
{
    return $this->posts()->paginate(10);
}

Pros: Paginates very easily; does not interfere with normal posts relationship.
Cons: Eager loading posts will not affect this accessor; running it will create two additional queries on top of the eager loaded query.

Option 2: Paginate the posts Collection returned by the eager-loaded relationship.

/**
 * Paginated posts accessor. Access via $topic->posts_paginated
 * 
 * @return \Illuminate\Pagination\Paginator
 */
public function getPostsPaginatedAttribute()
{
    $posts = $this->posts;

    // Note that you will need to slice the correct array elements yourself.
    // The paginator class will not do that for you.

    return \Paginator::make($posts, $posts->count(), 10);
}

Pros: Uses the eager-loaded relationship; creates no additional queries.
Cons: Must retrieve ALL elements regardless of current page (slow); have to build the current-page elements manually.

Leave a Comment