PHP
Filtering Eloquent Models by Related Model Conditions with `whereHas`
Learn how to filter your main Eloquent models based on the existence or specific conditions of their related models using `whereHas` and `orWhereHas` methods in Laravel.
<?php
// Assume you have Post and Comment models with a one-to-many relationship:
// Post hasMany Comments
// Comment belongsTo Post
// In your Post model (app/Models/Post.php)
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $fillable = ['title', 'content'];
public function comments()
{
return $this->hasMany(Comment::class);
}
}
// In your Comment model (app/Models/Comment.php)
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
protected $fillable = ['post_id', 'body', 'is_approved'];
public function post()
{
return $this->belongsTo(Post::class);
}
}
// Example Usage in a controller or route
use App\Models\Post;
// Find all posts that have at least one comment
$postsWithComments = Post::whereHas('comments')->get();
// Find all posts that have at least one APPROVED comment
$postsWithApprovedComments = Post::whereHas('comments', function ($query) {
$query->where('is_approved', true);
})->get();
// Find all posts that have at least one comment from a specific user (user_id = 5)
$postsWithUserComments = Post::whereHas('comments', function ($query) {
$query->where('user_id', 5);
})->get();
// Find all posts that DO NOT have any comments
$postsWithoutComments = Post::doesntHave('comments')->get();
// Find all posts that have at least one approved comment OR have no comments at all
$complexFilter = Post::whereHas('comments', function ($query) {
$query->where('is_approved', true);
})->orDoesntHave('comments')->get();
How it works: The `whereHas` method allows you to retrieve parent models based on conditions applied to their related models. It's incredibly useful for filtering. For instance, you can find all `Post` models that have *any* associated `Comment`, or more specifically, posts that have *approved* comments. `doesntHave` is its inverse, finding parent models without any related models. These methods generate efficient SQL queries, often involving `EXISTS` subqueries, to filter the main table without necessarily loading all related data into memory.