jmcondeprol's avatar

Join many-to-many lost after adding orderBy

Hi! Today I was trying to make some of my queries more efficient with some select clauses, ended up with this one:

Post::query() ->join('post_tag', 'posts.id', '=', 'post_tag.post_id') ->with(['author:id,name', 'tags:id,name']) ->select('title', 'author_id', 'post_tag.id', 'posts.created_at') ->simplePaginate(10);

Everything is working as expected, but... If I just add some orderBy (with ->latest() for example) my 'tags' property becomes an empty array.

Is there any way to achieve this preserving the related data of the join?

Post::query() ->join('post_tag', 'posts.id', '=', 'post_tag.post_id') ->with(['author:id,name', 'tags:id,name']) ->select('title', 'author_id', 'post_tag.id', 'posts.created_at') ->latest() // This causes the tags relationship to become empty ->simplePaginate(10);

0 likes
2 replies
LaryAI's avatar
LaryAI
AI
Best Answer
Level 58

The issue you're encountering is related to how Eloquent handles eager loading (with) in combination with joins and ordering. When you use a join, you're essentially flattening the result set, which can interfere with Eloquent's ability to correctly map related models.

To solve this, you can separate the join logic from the eager loading logic. Instead of using a join, you can use Eloquent's relationship methods to maintain the integrity of the relationships. Here's how you can modify your query:

Post::with(['author:id,name', 'tags:id,name'])
    ->select('id', 'title', 'author_id', 'created_at')
    ->latest()
    ->simplePaginate(10);

Explanation:

  1. Eager Loading: By using with(['author:id,name', 'tags:id,name']), you're telling Eloquent to load the author and tags relationships. This ensures that the related data is fetched in a separate query, preserving the relationship data.

  2. Select Statement: Ensure that you include the id of the posts table in your select statement. This is crucial because Eloquent needs the primary key to map the relationships correctly.

  3. Ordering: Use latest() to order the posts by the created_at column. This method is a shorthand for orderBy('created_at', 'desc').

By structuring your query this way, you maintain the integrity of the relationships while still applying the desired ordering. This approach leverages Eloquent's capabilities to handle relationships more effectively than a raw join would.

jmcondeprol's avatar

Nice one Lary! Haha. Didn't know that I also had to include the 'id' of the post in the select clause for the tags relationship to be retrieved. Thanks!

Please or to participate in this conversation.