Статьи

Быстрый совет Laravel: привязка модельного маршрута

Одна из замечательных возможностей Laravel — это простой в использовании компонент маршрутизации. Он предлагает простые URL-адреса, параметры, группирование, присвоение имен и группы маршрутизации для защиты событий, чтобы назвать несколько различных опций.

Laravel Logo

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

Route::group(['namespace' => 'Admin', 'prefix' => 'admin', 'middleware' => 'admin'], function () { Route::resource('categories', 'CategoriesController'); }); 

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

 public function edit($id) { $category = Category::find($id); if (!$category) { return redirect()->route('admin.categories.index')->withErrors([trans('errors.category_not_found')]); } // ... } 

Привязка модели

Это обычный способ сделать это, но у Laravel также есть более хороший способ оптимизировать эту повторяющуюся задачу, называемую привязкой модели маршрута . Вы в основном вводите подсказку название модели вместо параметра ID.

Если вы перечислите доступные маршруты, это должно выглядеть примерно так.

 +--------+-----------+------------------------------------+------------------------------------+----------------------------------------------------------------------+-----------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+------------------------------------+------------------------------------+----------------------------------------------------------------------+-----------------+ | | GET|HEAD | admin/categories | admin.categories.index | App\Http\Controllers\Admin\CategoriesController@index | web,admin | | | POST | admin/categories | admin.categories.store | App\Http\Controllers\Admin\CategoriesController@store | web,admin | | | GET|HEAD | admin/categories/create | admin.categories.create | App\Http\Controllers\Admin\CategoriesController@create | web,admin | | | GET|HEAD | admin/categories/{categories} | admin.categories.show | App\Http\Controllers\Admin\CategoriesController@show | web,admin | | | PUT|PATCH | admin/categories/{categories} | admin.categories.update | App\Http\Controllers\Admin\CategoriesController@update | web,admin | | | DELETE | admin/categories/{categories} | admin.categories.destroy | App\Http\Controllers\Admin\CategoriesController@destroy | web,admin | | | GET|HEAD | admin/categories/{categories}/edit | admin.categories.edit | App\Http\Controllers\Admin\CategoriesController@edit | web,admin | 

Вы можете видеть, что параметром маршрутизации является {categories} который вы можете оставить так, если хотите. Тем не менее, Laravel предоставляет возможность изменить его.

 Route::resource('categories', 'CategoriesController', [ 'parameters' => 'singular', ]); 
 +--------+-----------+------------------------------------+------------------------------------+----------------------------------------------------------------------+-----------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+------------------------------------+------------------------------------+----------------------------------------------------------------------+-----------------+ | | GET|HEAD | admin/categories | admin.categories.index | App\Http\Controllers\Admin\CategoriesController@index | web,admin | | | POST | admin/categories | admin.categories.store | App\Http\Controllers\Admin\CategoriesController@store | web,admin | | | GET|HEAD | admin/categories/create | admin.categories.create | App\Http\Controllers\Admin\CategoriesController@create | web,admin | | | GET|HEAD | admin/categories/{category} | admin.categories.show | App\Http\Controllers\Admin\CategoriesController@show | web,admin | | | PUT|PATCH | admin/categories/{category} | admin.categories.update | App\Http\Controllers\Admin\CategoriesController@update | web,admin | | | DELETE | admin/categories/{category} | admin.categories.destroy | App\Http\Controllers\Admin\CategoriesController@destroy | web,admin | | | GET|HEAD | admin/categories/{category}/edit | admin.categories.edit | App\Http\Controllers\Admin\CategoriesController@edit | web,admin | 

Примечание: Laravel 5.3 использует единственное число по умолчанию.

 public function edit(Category $category) { return view('admin.categories.edit', [ 'category' => $category ]); } 

Теперь Laravel автоматически разрешит категорию, используя параметр ID, и выдаст исключение, если модель не существует.

Примечание. Для разрешения параметра используется findOrFail Eloquent findOrFail , если для параметра не findOrFail значение по умолчанию.

На данный момент все выглядит отлично, потому что мы убрали проверку модели из всех методов. Тем не менее, нам все еще нужно поймать исключение и принять надлежащие меры.

Обработка исключений

Метод App\Exceptions\Handler@render отвечает за преобразование исключений в ответ HTTP. Мы будем использовать его для обработки ModelNotFoundException и перенаправления на страницу 404 не найдена.

У метода render есть параметр запроса и исключения, который мы можем использовать, чтобы определить, что делать.

 public function render($request, Exception $e) { if ($e instanceof ModelNotFoundException) { $view = view("admin.404"); if ($e->getModel() == Category::class) { $view->withErrors(['Category not found'])->render(); } return response($view, 404); } else { // handle other exceptions return parent::render($request, $e); } } 

Мы проверяем, является ли выброшенное исключение экземпляром ModelNotFoundException . Мы также можем проверить название модели, чтобы отобразить правильное сообщение об ошибке. Чтобы избежать добавления нескольких тестов if для всех наших моделей, мы можем создать индексированный массив сообщений и использовать имя класса модели, чтобы получить правильное сообщение.

Разрешение параметров

Laravel разрешает параметры маршрутизации, используя подсказки имени и типа. Если тип параметра является моделью, он пытается найти запись в базе данных с использованием идентификатора и завершается ошибкой, если записи не найдены.

Как хорошая практика, мы стараемся не раскрывать наши внутренние идентификаторы конечному пользователю, и эта проблема часто решается с помощью универсально уникальных идентификаторов (UUID). Но поскольку Laravel использует первичный ключ таблицы для разрешения связанного параметра, он всегда выдаст ошибку!

Чтобы решить эту проблему, Laravel позволяет нам переопределить метод getRouteKeyName из класса родительской модели. Метод должен вернуть имя атрибута, в данном случае uuid .

 class Category extends Model { // ... public function getRouteKeyName() { return "uuid"; } } 

Теперь, если мы попытаемся отредактировать определенную категорию, используя UUID, она должна работать как положено, например, http://local.dev/admin/categories/b86266d4-63c7-11e6-8c98-08002751e440/edit .

Есть еще советы Laravel? Поделись с нами!