Квест → Как хакнуть форму
Прошли: 77
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, но здесь есть две проблемы:
Job
загружает много заданий и не использует привязку к одной модели Job
, так что мне здесь такое отношение не нужно.Этот второй момент заставил меня задуматься: “Есть ли способ подключиться к модели, когда она подставляется из маршрута?”
Ответ: Да!
Модели Eloquent имеют функцию, resolveRouteBinding
, которая может быть перезаписана с целью добавления дополнительного функционала.
Поскольку это произойдет только тогда, когда в маршруте будет запрошено конкретное задание, он полностью исключит индексную страницу задания, решая вторую проблему, которая возникла с жадной загрузкой по умолчанию.
Нужная мне функция выглядит так:
public function resolveRouteBinding($value, $field = null) { return $this->where('id', $value) ->withCount('notes') ->firstOrFail(); }
Сначала мы ищем фактическую модель, так как передаётся id
. Затем проверяем, что загружаем ее с нужным подсчетом и что получаем только одну модель! Теперь модель загружена с доступным полем notes_count
- с данными, которые нам нужны по умолчанию. Мы не загрузили абсолютно все заметки, что решает первую проблему, с которой я столкнулся в процессе жадной загрузки по умолчанию.
Это обычный PHP-блок, поэтому я полагаю, что можно добавить больше функционала для поддержки дополнительных загрузок в зависимости от маршрута.
Использование данного подхода означало, что вместо добавления $job->loadCount('notes')
к каждому методу контроллера, (более 40 раз только для начала!) я просто добавил несколько строк в код своей модели. Это гораздо проще в поддержке.