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

Laravel withWhereHas(): не дублюйте умову в with() і whereHas()

Як замінити однакову умову в whereHas() і with() на withWhereHas(), одночасно відфільтрувати батьківські моделі та завантажити лише відповідні пов'язані записи.

Іноді потрібно одночасно зробити дві речі: вибрати тільки ті моделі, у яких є пов'язані записи з певною умовою, і завантажити тільки ці пов'язані записи.

Якщо окремо використовувати whereHas() і with(), одна й та сама умова легко з'являється у коді двічі. Для такого випадку Laravel має withWhereHas().

Замість однакової умови у whereHas() і 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();

Обидва варіанти мають однаковий намір: отримати тільки дописи, у яких є схвалені коментарі, і одразу завантажити тільки схвалені коментарі.

З withWhereHas() умова описана один раз. Якщо її потрібно змінити, немає ризику виправити whereHas() і забути про таку саму умову в with().

Що саме робить withWhereHas()

PHP - Одна умова для фільтрації та завантаження зв'язку
$posts = Post::query()    ->withWhereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->get();

withWhereHas() використовує одну й ту саму умову у двох місцях:

  • залишає тільки ті Post, які мають схвалені коментарі;
  • попередньо завантажує тільки схвалені comments.

Тому після виконання запиту в $post->comments будуть саме ті коментарі, які відповідають умові.

Основна користь withWhereHas() тут не в тому, щоб зробити один SQL-запит. Він прибирає дублювання умови і зменшує ризик того, що фільтрація та завантаження зв'язку з часом почнуть працювати по-різному.

Коли достатньо whereHas()

PHP - Тільки відфільтрувати дописи
$posts = Post::query()    ->whereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->get();

Якщо потрібно лише знайти дописи, які мають схвалені коментарі, але самі коментарі зараз не потрібні, достатньо whereHas().

Коли достатньо with()

PHP - Тільки завантажити схвалені коментарі
$posts = Post::query()    ->with([        'comments' => fn ($query) => $query            ->where('approved', true),    ])    ->get();

Якщо потрібно отримати всі дописи, але для кожного завантажити тільки схвалені коментарі, використовуйте with(). Наявність схваленого коментаря в цьому випадку не впливає на те, чи потрапить сам допис у результат.

withWhereHas() потрібний, коли треба обидві дії

PHP - Фільтрація і завантаження з однією умовою
$posts = Post::query()    ->withWhereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->get();

Це саме той випадок, для якого withWhereHas() читається найкраще: зв'язок впливає і на відбір батьківських моделей, і на те, які пов'язані записи потрібно завантажити.

Якщо умови навмисно різні, не об'єднуйте їх

PHP - Різні умови мають залишатися окремими
$posts = Post::query()    ->whereHas(        'comments',        fn ($query) => $query            ->where('approved', true),    )    ->with([        'comments' => fn ($query) => $query            ->latest(),    ])    ->get();

Тут логіка вже інша: допис повинен мати хоча б один схвалений коментар, але завантажити потрібно всі коментарі, відсортовані від нових до старих.

У такому випадку окремі whereHas() і with() правильніші. withWhereHas() не потрібно використовувати лише заради коротшого запису, якщо умови насправді різні.

Спільна умова повинна мати однаковий зміст в обох місцях

Функція зворотного виклику в withWhereHas() використовується і для перевірки зв'язку, і для його попереднього завантаження.

Тому сюди добре підходять саме спільні умови відбору: where(), scopes та інші правила, які повинні однаково діяти в обох частинах запиту.

withWhereHas() варто використовувати тоді, коли одна й та сама умова повинна одночасно визначати, які батьківські моделі потраплять у результат і які пов'язані записи будуть завантажені. Один намір - одна умова.

#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