PHP
Filter Models Based on Related Model Existence with `whereHas`
Learn to use Laravel Eloquent's `whereHas` method to filter parent models based on conditions of their related models, enabling complex data retrieval.
// app/Models/Post.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function comments()
{
return $this->hasMany(Comment::class);
}
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
// app/Models/Comment.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
protected $fillable = ['content', 'is_approved', 'post_id'];
}
// app/Models/Tag.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Tag extends Model
{
protected $fillable = ['name'];
}
// Usage example: Get posts that have at least one comment
$postsWithComments = Post::has('comments')->get();
// Usage example: Get posts that have at least one approved comment
$postsWithApprovedComments = Post::whereHas('comments', function ($query) {
$query->where('is_approved', true);
})->get();
// Usage example: Get posts that have a specific tag named 'Laravel'
$postsWithLaravelTag = Post::whereHas('tags', function ($query) {
$query->where('name', 'Laravel');
})->get();
// Usage example: Get posts that *do not* have any comments
$postsWithoutComments = Post::doesntHave('comments')->get();
// Usage example: Get posts that *do not* have any approved comments
$postsWithoutApprovedComments = Post::whereDoesntHave('comments', function ($query) {
$query->where('is_approved', true);
})->get();
How it works: The `whereHas` method allows you to filter results of a parent model based on conditions applied to its related models. This is highly useful when you need to retrieve, for example, all posts that have comments by a specific user or posts that are associated with a particular tag. `has()` checks for the mere existence of a related model, while `whereHas()` allows adding specific constraints to the related model's query. `doesntHave()` and `whereDoesntHave()` provide the inverse functionality. This is distinct from eager loading relationships (using `with()`) as it focuses on filtering the *parent* models, not fetching the related data for each parent.