Laravel multiple whereHas criteria for relationship
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
Mastering Multi-Criteria Filtering in Laravel Eloquent with whereHas
As developers working with relational databases through an ORM like Eloquent, one of the most common tasks is filtering parent models based on complex conditions existing within their related child models. When you need to ensure that a parent record is associated with multiple distinct criteria across its relationships—an "AND" condition across multiple whereHas clauses—it can quickly become confusing.
This post dives into a specific challenge involving nested relationships and demonstrates the proper, clean way to achieve complex filtering using Eloquent’s powerful whereHas method.
The Scenario: Finding Contacts with Specific Visit History
Let's look at the data structure you provided:
Contacts Table:
| id | name |
|---|---|
| 1 | Joe |
| 2 | Sally |
Visits Table:
| id | contact_id | pathname | referrer |
|---|---|---|---|
| 1 | 1 | about | |
| 2 | 1 | pricing | null |
| 3 | 1 | signup | null |
| 4 | 2 | about | null |
| 5 | 2 | signup | null |
The Goal: Retrieve all contacts that have both visited the 'signup' page AND were referred by 'google'. This is a strict AND condition.
Why Simple Chaining Fails
You attempted to use chaining like this:
Contact::whereHas('visits', function($query) {
$query->where('pathname', '=', 'signup');
})
->orWhereHas('visits', function($query) {
$query->where('referrer', '=', 'google');
})->get();As you correctly observed, this structure uses orWhereHas. This tells Eloquent: "Find contacts that have either a visit to 'signup' OR a visit referred by 'google'." This retrieves contacts like Sally (who visited signup but wasn't referred by google), which is not what we need.
To enforce an AND relationship—where the contact must satisfy Condition A and Condition B simultaneously within its related records—we need to nest the conditions inside a single query scope.
The Correct Approach: Nested whereHas for AND Logic
The solution lies in ensuring that both required criteria are evaluated against the same parent relationship using nested closures. By applying multiple whereHas calls within the same outer closure, we instruct Eloquent to look for related records that satisfy all specified conditions simultaneously.
To find contacts who have visited 'signup' AND were referred by 'google', we structure the query as follows:
$contacts = Contact::whereHas('visits', function ($query) {
// Condition 1: The contact must have at least one visit where pathname is 'signup'
$query->where('pathname', '=', 'signup');
})->whereHas('visits', function ($query) {
// Condition 2: AND the contact must also have at least one visit where referrer is 'google'
$query->where('referrer', '=', 'google');
})->get();Explanation of the Technique
- Outer
whereHas: We start by scoping the query onto thevisitsrelationship. - First Closure (
pathname = 'signup'): The first closure filters thevisitstable to ensure that the contact has at least one associated visit record wherepathnameequals'signup'. - Second
whereHas: Crucially, we chain a secondwhereHascall. Since both clauses operate on the same parent relationship (contacts), Eloquent implicitly combines these requirements with an AND. It forces the query to find contacts that satisfy both criteria within their related visits.
This method is idiomatic for building complex relational filters in Laravel, making your code highly readable and maintainable. For deeper dives into advanced querying techniques, understanding how Eloquent translates these methods into optimized SQL joins is key—a concept central to modern database interaction, much like the principles behind efficient data retrieval on platforms like Laravel Company.
Conclusion
When dealing with multi-faceted requirements involving relationships, resist the urge to chain simple whereHas calls with orWhereHas. Instead, embrace nested logical grouping using multiple closures within a single whereHas scope. This pattern correctly enforces the necessary AND logic required for complex data retrieval. By mastering this technique, you can move beyond simple filtering and build sophisticated querying capabilities in your Laravel applications.