Search code examples
laravelpaginationeloquenteager-loading

Laravel Eloquent: Order results by eaged loaded model.property, and paginate the result


Lets say I have three models: Post, Category and Tag.

The Post belongsTo Category and Category hasMany Post.

Theres manyToMany relation between Tag and Category.

I want to list my posts by Category name and paginate the results.

Post::with('category','category.tags')
  ->orderBy('category.name') //this is the bogus line
  ->paginate(10);

But this syntax doesn't work.


What I tried is this as:

Post::select('categories.*')
  ->join('categories','posts.category_id','=','categories.id')
  ->orderBy('categories.name)
  ->paginate(10);

But then I lose the eager loaded data.

If I drop the select() clause then I get rubbish data as categories.id overwrites posts.id. See here.


Is there any elegant way to solve this issue? After spending hours on this I'm one step away from iterating through paginated posts and 'manually' loading the relations as:

foreach($posts as $post) {
  $post->load('category','category.tags');
}

Not even sure if there's downside to this but it doesn't seem right. Please correct me if I'm wrong.

UPDATE on last step: Eager loading on paginated results won't work so if I go that road I'll need to implement even uglier fix.


Solution

  • You should be able to use both join and with.

    Post::select('posts.*') // select the posts table fields here, not categories
    ->with('category','category.tags')
    ->join('categories','posts.category_id','=','categories.id')
    ->orderBy('categories.name)
    ->paginate(10);
    

    remember, the with clause does not alter your query. Only after the query is executed, it will collect the n+1 relations.

    Your workaround indeed loses the eager loading benefits. But you can call load(..) on a collection/paginator (query result) as well, so calling ->paginate(10)->load('category','category.tags') is equivalent to the query above.