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! 🚀