PHP Profi

Жадная загрузка в моделях маршрутов Перевод

TLDR: перезапишите метод resolveRouteBinding в вашей Eloquent-модели для жадной загрузки (eager loading) отношений, которые понадобится использовать.

Проблема: ограничения на привязку модели к маршруту

В приложении Laravel, которое я пишу, у меня есть несколько десятков маршрутов, у которых в целом одинаковый формат: /jobs/{job} в качестве префикса, а затем определенные действия для этой задачи. В Laravel у нас есть привязка модели к маршруту, то есть, по общей конвенции, вы можете использовать имя модели (в данном случае - модель Job) в фигурных скобках вашего маршрута, и когда ваше приложение достигает метода контроллера, модель отправляется в качестве параметра.

На примере /jobs/{job} это означает, что моя сигнатура метода контроллера будет выглядеть следующим образом:

public function show(Job $job){ /* ... */ }

Это крутой функционал, который ограждает нас от постоянного написания однообразного шаблонного кода по поиску Eloquent-модели в базе непосредственно в методах наших контроллеров.

Я работаю над добавлением заметок к каждому заданию, что удается при помощи новой модели JobNote, которая имеет отношение "одна ко многим" с каждым Job.

Теперь, я хотел бы отобразить в своей поднавигации подсчет количества JobNotes в каждом Job для всех этих заданий (Jobs). В Laravel мы можем напрямую загрузить счетчик, запустив:

$job->loadCount('notes')

в модели задания, которую я получаю в качестве параметра.

Проблема заключается в следующем: у меня есть более 40 маршрутов, которые используют ровно такой же префикс модели задания, и они все нуждаются в подсчете, так как все маршруты используют одну и ту же поднавигацию, в которой будет использоваться подсчет. Это означало бы, что я должен добавить эту строку в более чем 40 мест в коде (единожды для каждого метода контроллера), а также не забывать добавлять ее, когда я пишу новые методы контроллера в этой группе маршрутов.

Не так я себе представляю простой и поддерживаемый опыт кодинга.

Первая Идея: Жадная Загрузка по умолчанию

Eloquent имеет возможность всегда загружать отношения по умолчанию, используя переменную protected $with в моей модели Eloquent, но здесь есть две проблемы:

  1. Мне нужен только подсчет, а не набор всех связанных заметок.
  2. Маршрут индексов Job загружает много заданий и не использует привязку к одной модели Job, так что мне здесь такое отношение не нужно.

Этот второй момент заставил меня задуматься: “Есть ли способ подключиться к модели, когда она подставляется из маршрута?”

Ответ: Да!

Реальное решение: resolveRouteBinding

Модели Eloquent имеют функцию, resolveRouteBinding, которая может быть перезаписана с целью добавления дополнительного функционала.

Поскольку это произойдет только тогда, когда в маршруте будет запрошено конкретное задание, он полностью исключит индексную страницу задания, решая вторую проблему, которая возникла с жадной загрузкой по умолчанию.

Нужная мне функция выглядит так:

public function resolveRouteBinding($value, $field = null)
{
    return $this->where('id', $value)
              ->withCount('notes')
              ->firstOrFail();
}

Сначала мы ищем фактическую модель, так как передаётся id. Затем проверяем, что загружаем ее с нужным подсчетом и что получаем только одну модель! Теперь модель загружена с доступным полем notes_count- с данными, которые нам нужны по умолчанию. Мы не загрузили абсолютно все заметки, что решает первую проблему, с которой я столкнулся в процессе жадной загрузки по умолчанию.

Это обычный PHP-блок, поэтому я полагаю, что можно добавить больше функционала для поддержки дополнительных загрузок в зависимости от маршрута.

Использование данного подхода означало, что вместо добавления $job->loadCount('notes') к каждому методу контроллера, (более 40 раз только для начала!) я просто добавил несколько строк в код своей модели. Это гораздо проще в поддержке.

2020-11-19 оригинал

Последние посты

Комментарии

авторизуйтесь или зарегистрируйтесь, чтобы оставить комментарий