laravel whereHas include no relation
Stefan Bogdanescu
Founder & Senior Architect · 2026-06-29
# Mastering Eloquent Relations: Querying Relationships When Data May Be Missing
As a senior developer working with Laravel, one of the most common hurdles we face when querying Eloquent relationships is understanding how `whereHas` behaves, especially when dealing with optional or many-to-many associations. This post dives deep into a tricky scenario: how can we query models based on a relationship while still including records that have absolutely no related entries?
## The Scenario: Stores and Products
Let’s establish the context using two simple models: `Store` and `Product`, connected via a many-to-many pivot table named `product_store`.
```php
// Store Model
class Store extends Model
{
public function products()
{
return $this->belongsToMany(Product::class);
}
}
// Product Model
class Product extends Model
{
public function stores()
{
return $this->belongsToMany(Store::class);
}
}
```
We have a situation where a `Product` might exist in the database but not be associated with any `Store`. We want to retrieve *all* products, regardless of whether they have an associated store.
## The Pitfall of `whereHas`
The standard way to check for the existence of a relationship is using `whereHas`. As noted in the Laravel documentation, this method implicitly performs an `INNER JOIN` operation against the related table. This means that if you use it, Eloquent will only return models where the relationship *exists*.
Consider your current approach:
```php
// Attempting to find Products that have at least one Store
$productsWithStores = Product::whereHas('stores', function ($query) {
$query->where('stores.id', '=', $storeId); // Example filtering by a specific store ID
})->get();
```
As the Laravel docs correctly point out, this query strictly retrieves products that are linked to at least one store. It excludes any product records that have no corresponding entry in the pivot table. This is useful for finding *related* data, but it fails when your goal is to retrieve the entire set of parent models.
## The Solution: Using `whereDoesntHave` and Direct Queries
If your objective is to query all products, including those with zero stores, you need a strategy that avoids filtering based on relationship existence. There are two primary ways to achieve this, depending on your ultimate goal:
### 1. Retrieving All Products (Ignoring Relationships)
If the requirement is simply to fetch every `Product` record in the database, irrespective of its store association, you should bypass any relationship constraints entirely and query the model directly.
```php
// This retrieves ALL products, with or without store associations.
$allProducts = Product::all();
```
This is the most straightforward solution when you simply need a complete list of parent records. If you are only performing simple retrieval, there is no need to involve Eloquent relationship querying mechanisms.
### 2. Finding Products *Without* Relationships (Exclusion)
If your goal shifts—for example, finding products that *do not* belong to any store—then the `whereDoesntHave` method becomes incredibly useful. This uses a `LEFT JOIN` internally and filters out the results where no match is found in the related table.
```php
// Find all products that do NOT have any associated stores
$productsWithoutStores = Product::whereDoesntHave('stores')->get();
```
This effectively leverages the underlying database structure to perform an exclusion check, ensuring you retrieve only those records for which no relationship exists. When structuring complex queries involving optional associations, mastering methods like `whereHas` (for inclusion) and `whereDoesntHave` (for exclusion) is key to writing efficient and accurate Eloquent code, as promoted by the principles found on resources like https://laravelcompany.com.
## Conclusion
Understanding the nuances between checking for existence (`whereHas`) and checking for non-existence (`whereDoesntHave`) is crucial for advanced Eloquent usage. When dealing with optional relationships in a many-to-many setup, remember that default behavior often leans towards an `INNER JOIN`. By consciously choosing whether you need to *include* or *exclude* related records, you can write precise queries that map exactly to your business logic, ensuring your data retrieval is both accurate and efficient.