Introduction
Sometimes when I'm working on Laravel projects, I want to grab specific models and their relationships, but only if the relationship isn't empty. For example, let's say that in my project, I want to grab all of the User models as well as eager load their comments (so we can avoid an N+1 issue); but only the users that have actually commented. To do this, we would need to use the with and whereHas methods together in our query.
As of Laravel 9.16.0, thanks to a cool new addition from @eusonlito in PR #42597, we don't need to make these two calls ourselves anymore, and we can make use of the withWhereHas method.
In this Quickfire post, we're going to take a quick look at the withWhereHas method and how you can use it to replace separate with and whereHas method calls in your queries.
Using "withWhereHas"
Let's take our example from above and say that we want to grab only the users that have commented; and we also want to eager load their comments. To do this, our query might look something like this:
1$usersWithApprovedComments = User::whereHas('comments')2 ->with('comments')3 ->get();
However, with the new withWhereHas method, we could clean up the query like so:
1$usersWithApprovedComments = User::withWhereHas('comments')->get();
As you can see, this new method provides some syntactic sugar to help improve the readability of our code.
As a side note, if you're unfamiliar with eager loading and how it can prevent N+1 issues in your code, you can check out my How to Force Eager Loading and Prevent N+1 Issues in Laravel article.
Using "withWhereHas" with Callbacks
The withWhereHas method also supports callbacks so that you can add extra logic to your eager loading and checks. For example, if we wanted to extend our example queries from above and only fetch the users that had approved comments (and eager only the approved comments), our code might look like this:
1use Illuminate\Contracts\Database\Eloquent\Builder;2 3$usersWithApprovedComments = User::whereHas(4 'comments', fn (Builder $query): Builder => $query->where('approved', true)5 )6 ->with([7 'comments' => fn (Builder $query): Builder => $query->where('approved', true)8 ])9 ->get();
We can then improve the code by using the withWhereHas method like so:
1use Illuminate\Contracts\Database\Eloquent\Builder;2 3$usersWithApprovedComments = User::withWhereHas(4 'comments',5 fn (Builder $query): Builder => $query->where('approved', true)6)->get();
In my opinion, I think this method is a really cool addition that helps to reduce the duplication; especially when using the callbacks in the queries.
Conclusion
Hopefully, this short Quickfire post should have given you a brief insight into how you can use the withWhereHas method in your Laravel applications to reduce duplication.
If you enjoyed reading this post, I'd love to hear about it. Likewise, if you have any feedback to improve the future ones, I'd also love to hear that too.
If you're interested in getting updated each time I publish a new post, feel free to sign up for my newsletter below.
Keep on building awesome stuff! 🚀