/ laravel

Привязка модели к маршрутам 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-функция. Теперь вы знаете как привязать модель к ключу маршрута, как явно привязав модель к парамтру, так и неявно. А так, же, выяснили, почему модель при привязке к маршруту возвращает пустой массив. Теперь ваш код станет на несколько строк меньше, а контроллеры чище :)