Laravel 4.1: How to paginate eloquent eager relationship?

There was question about old L3 eager loaded paginations, not using eloquent. But i want to use eloquent to get eager loaded relationship with pagination.

Main model: Topic that has one to many relationship with Posts, So that one Topic has many Posts. I get all data with this function:

public function findById($id)
{
    return $this->topic->with('posts', 'posts.user', 'posts.user.profile')
                       ->find($id);
}

And later I make loop to display all results, but they are not paginated:

@foreach($topic->posts as $post)
... unpaginated content ...
@endforeach

So, i could make a workaround and select separately all posts that has $id of topic and use ->paginate() instead of ->get() and would got paginated $pots,

  1. but is there possibility to use eager loaded relationship posts that is paginated ? And how can it be done ?

Solution 1:

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.

Solution 2:

You may want to take a look at following:

$category = 'Laravel';

$posts = Post::with('user', 'category')->whereHas('category', function($query) use ($category) {
$query->where('name', '=', $category);
})->paginate();

By Zenry : Source:http://laravel.io/forum/02-25-2014-eager-loading-with-constrains-load-post-based-on-category-name

Hope this helps