Updated June 2026. Tested on Laravel 13 and PHP 8.4.

Often you want records based on something about their related records: posts that have comments, users who have placed an order, comments whose parent matches a condition. Eloquent does this without you writing a manual join, using relationship existence queries. Here are the ones you reach for, including the polymorphic version.

has and doesntHave

The simplest case is "does this relationship have any records". has keeps models that do, doesntHave keeps those that do not.

// posts that have at least one comment
$posts = Post::has('comments')->get();

// posts with no comments
$posts = Post::doesntHave('comments')->get();

// posts with more than three comments
$posts = Post::has('comments', '>', 3)->get();

whereHas

When you need a condition on the related records, use whereHas. It takes the relationship and a closure that constrains it.

// posts that have a comment containing the word "laravel"
$posts = Post::whereHas('comments', function ($query) {
    $query->where('body', 'like', '%laravel%');
})->get();

There is a shorter form for a single simple condition.

$posts = Post::whereRelation('comments', 'body', 'like', '%laravel%')->get();

whereHasMorph for polymorphic relationships

Polymorphic relationships need their own version, because one relationship points at several different models. Say a Comment can belong to a Blog or a News (a commentable morph). To get comments whose parent matches a condition, use whereHasMorph.

use App\Models\Comment;

$comments = Comment::whereHasMorph(
    'commentable',
    [Blog::class, News::class],
    function ($query) {
        $query->where('title', 'like', 'foo%');
    }
)->get();

You can also vary the condition per related type, by accepting the $type argument.

$comments = Comment::whereHasMorph(
    'commentable',
    [Post::class, Video::class],
    function ($query, string $type) {
        $query->where('title', 'like', 'foo%');

        if ($type === Post::class) {
            $query->orWhere('content', 'like', 'foo%');
        }
    }
)->get();

And the inverse, comments whose parent is not one of the given types, with whereDoesntHaveMorph.

$comments = Comment::whereDoesntHaveMorph(
    'commentable',
    [Blog::class, News::class]
)->get();

If you want to match any of the morph types, pass '*' instead of a list, and Laravel works out the types from the data.

This is one of the nicer parts of Eloquent. Instead of hand writing joins to filter by a relationship, you describe what the relationship should contain and Laravel builds the query. For a refresher on setting up the polymorphic relationship itself, see the post on polymorphic relationships. Questions welcome in the comments.