Привязка модели к маршрутам Laravel. Явная, неявная привязка (Route Model Binding)
Laravel фреймворк имеет в своём арсенале много полезных функций. Laravel - это мощный инструмент, который позволяет создавать всё, начиная от лендинга, до API, или консольного приложения. Фреймворк привносит много новых функций, многие из которых нацелены на упрощение написания кода программистам. И очень важной функцией является привязка модели к маршруту (Route Model Binding), которую я рассмотрю в этой статье.
Вероятно, вы уже использовали эту возможность, и пользовались ею. Однако, уверен, что у вас ещё остались некоторые вопросы, как это реализовано внутри. Сегодня я подробно покажу, как работать с Resolve Route Binding, и отвечу на все вопросы, которые могут вас интересовать.
Что это такое?
Привязка модели к маршруту - это функций фреймворка, реализующая механизм внедрения экземпляра модели по ключу маршрута. Сложно описано, но на деле, просто.
Например, представим, что у нас есть блог, с соответствующими маршрутами, моделями. И статья расположена по адресу blog/{id}
И правило для маршрута будет выглядеть так:
Route::get('blog/{id}', function($id) {
$article = Article::find($id);
if(!$article) {
abort(404);
}
//или проще
$article = Article::findOrFail($id); // will abort 404
return view('blog.article', compact('article'));
});
Однако, используя внедрение моделей в маршруты, код можно упростить до:
Route::get('blog/{article}', function(\App\Article $article) {
return view('blog.article', compact('article'));
});
Этот код говорит Laravel - верни мне экземпляр модели \App\Article, идентификатор которой возьми из маршрута = {article}.
Важно, что название аргумента маршрута blog/
{article}
должно соответствовать имени переменной аргумента функции function(\App\Article$article
).
И только так фреймворк будет понимать, какой параметр отвечает какому классу.
Распространённая проблема: при несоблюдении правильного именования, вместо экземпляра класса будем получать пустой массив.
Всего в Laravel существует 2 вида привязок моделей: явная привязка и неявная привязка.
Этот пример показывает реализацию неявной привязки.
Неявная привязка модели
Неявная привязка соответствует той, что была описана выше. Суть её проста: именуете нужный параметр маршрута, прописываете аргумент в виде зависимости function(\App\Article $article
), и фреймворк сам парсит все данные, возвращая экземпляр этого класса.
Фреймворк по умолчанию берёт ключ из маршрута, и ищет в базе данных по полю id
. Однако, возможности фреймворка, без труда, позволяют изменить поле, по которому будет искаться запись.
Для того, чтобы изменить поле, по которому производится выборка, достаточно переопределить метод getRouteKeyName в вашей Eloquent моделе.
Например, можно задать, чтобы статьи искало по полю slug вместо id. Для этого, на предыдущем примере, в моделе \App\Article переопределю родительский метод
public function getRouteKeyName() {
// имя поля, по которому производится поиск
return 'slug'; // аналонично Article::where('slug', '=', ...)
}
И теперь, вместо старого варианта blog/5
URL-адрес будет иметь вид blog/article-alias
Явная привязка модели
Явная привязка модели отличается тем, что настройка производится "глобально" для системы. Подобная настройка производится в любом из сервис-провайдеров. То есть, нам нужно сконфигурировать маршрутизацию до того, как загрузится основное приложение.
Если вы создаёте какой-то модуль, или библиотеку, задействующую маршрутизацию, то можно создать свой сервис-провайдер, и записать туда всю логику по привязке модели:
Route::model('article', \App\Article::class);
Но, есть и второй способ привязки - прописывать всю логику в уже созданном провайдере - app/Providers/RouteServiceProvider
public function boot()
{
\\ привязка параметра к моделе
\Route::model('article', \App\Article::class);
parent::boot();
}
Кастомизация запроса
Бывают случаи, когда из базы данных нужно доставать только определённые записи. Например, добавим возможность получения только опубликованных записей, исключая черновики.
\Route::bind('article', function($value) {
// где в $value попадают данные из маршрута (в нашем примере - slug)
return \App\Article::where('alias', $value)->where('is_draft', false)->firstOrFail();
});
Метод
model
принимает строку - название модели,
Методbind
принимает функцию, которая возвращает модель
И, при таком подходе, уже не обязательно записывать модель, как зависимость. Теперь можно писать так:
Route::get('blog/{article}', function($article) {
// $article === \App\Article $article
return view('blog.article', compact('article'));
});
А фреймворк уже сам распарсит данные, и подставит вместо обычного идентификатора сконфигурированную модель.
Кастомизация исключений при привязке моделей
Кастомизация исключений является очень полезной штукой, особенно, при построении API. Laravel поддерживает чрезвычайно простой и удобный способ, чтобы это сделать.
Опять же, всё это настраивается с помощью фасада Route
. Я, как и раньше, буду производить настройку из RouteServiceProvider.
public function boot()
{
Route::model('article', Article::class, function() {
throw new NotFoundHttpException();
});
parent::boot();
}
Как видите, это уже знакомый метод model, который, оказывается, принимает 3 аргумент - функцию, который выполняется, в случае, если данные не удалось найти.
Резюме
Сегодня была рассмотрена очень удобная и поезная Laravel-функция. Теперь вы знаете как привязать модель к ключу маршрута, как явно привязав модель к парамтру, так и неявно. А так, же, выяснили, почему модель при привязке к маршруту возвращает пустой массив. Теперь ваш код станет на несколько строк меньше, а контроллеры чище :)