laravel-withwherehas-avoid-duplicate-conditions-tip.png
Шаблон кодуLaravel

Laravel withWhereHas(): Avoid Duplicating Conditions in with() and whereHas()

Replace the same condition repeated in whereHas() and with() with withWhereHas(), filtering parent models and eager loading only matching related records.

Sometimes a query needs to do two things at once: return only models that have related records matching a condition, and eager load only those matching related records.

Using separate whereHas() and with() calls can duplicate the same condition. Laravel provides withWhereHas() for this case.

Replace the Same Condition in whereHas() and with()

До
php
$posts = Post::query()    ->whereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->with([        'comments' => fn ($query) => $query            ->where('approved', true),    ])    ->get();
Після
php
$posts = Post::query()    ->withWhereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->get();

Both versions express the same intent: return only posts that have approved comments and eager load only approved comments.

With withWhereHas(), the condition is defined once. When it changes, there is no risk of updating whereHas() while leaving a different copy inside with().

What withWhereHas() Actually Does

PHP - One condition for filtering and eager loading
$posts = Post::query()    ->withWhereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->get();

withWhereHas() applies the same condition in two places:

  • it keeps only Post models that have approved comments;
  • it eager loads only the approved comments.

After the query runs, $post->comments contains the comments that match the condition.

The main benefit of withWhereHas() here is not turning the work into one SQL query. It removes a duplicated condition and reduces the risk that relationship filtering and eager loading drift apart over time.

When whereHas() Is Enough

PHP - Filter posts only
$posts = Post::query()    ->whereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->get();

If you only need posts that have approved comments and do not need the comments themselves right now, whereHas() is enough.

When with() Is Enough

PHP - Eager load approved comments only
$posts = Post::query()    ->with([        'comments' => fn ($query) => $query            ->where('approved', true),    ])    ->get();

If every post should be returned but each post should only eager load approved comments, use with(). The presence of an approved comment does not determine whether the post itself is returned.

Use withWhereHas() When You Need Both

PHP - Filter and eager load with one condition
$posts = Post::query()    ->withWhereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->get();

This is where withWhereHas() communicates the intent best: the relationship affects both which parent models are returned and which related records are loaded.

Keep Intentionally Different Conditions Separate

PHP - Different conditions should stay separate
$posts = Post::query()    ->whereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->with([        'comments' => fn ($query) => $query            ->latest(),    ])    ->get();

Here the intent is different: a post must have at least one approved comment, but all comments should be loaded from newest to oldest.

Separate whereHas() and with() calls are correct in this case. Do not use withWhereHas() only to make the code shorter when the conditions are not actually the same.

The Shared Condition Should Mean the Same Thing in Both Places

The callback passed to withWhereHas() is used both for relationship filtering and constrained eager loading.

Keep it focused on shared filtering rules such as where() clauses, scopes, and other constraints that should have the same meaning in both query contexts.

Use withWhereHas() when one relationship condition should define both which parent models are returned and which related models are loaded. One intent - one condition.

#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #where-has #with-where-has #eager-loading #relationships

Схожі поради

laravel-whererelation-replace-wherehas-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #where-relation #where-has #relationship-queries
laravel-latestofmany-latest-related-record-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #eager-loading #relationships #latest-of-many #one-of-many
laravel-tap-side-effects-keep-original-value-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #tap #helpers #tappable
laravel-firstorcreate-replace-first-create-tip.pngПорада
Переглянути пораду
#eloquent #laravel-12 #laravel-13 #php-8-4 #php-8-5 #model-persistence #first-or-create #first-or-new