Все мы используем простой синтаксис Route::get() и Route::post(), но в больших проектах это делать сложнее. В этой статье собраны несколько советов для различных ситуаций.
Совет 1. Route::get() ДО Route::resource()
Для Ресурсных контроллеров это одна из самых распространенных ошибок — смотрим пример:
Route::resource('photos', 'PhotoController');
Route::get('photos/popular', 'PhotoController@method');
Второй маршрут будет неточным, знаете почему? Потому, что он будет соответствовать методу show() из Route::resource() — соответственно /photos/{id}, который назначит popular в качестве параметра $id.
Поэтому, если вы хотите добавить любой get/post маршрут, в дополнение к Route::resource(), то размещайте их ДО ресурса. Например так:
Route::get('photos/popular', 'PhotoController@method');
Route::resource('photos', 'PhotoController');
Совет 2. Группа в другой группе
Практически все мы знаем, что можно группировать маршруты с помощью Route::group() и назначать различные мидлвары/префиксы и другие параметры.
А что делать, если нужен определенный набор правил для подгрупп этих групп?
Типичный пример: есть публичные и аутентифицированные маршруты, но в пределах группы с аутентификацией вам нужно отделить администраторов от обычных пользователей.
Можно сделать это так:
// публичные маршруты
Route::get('/', 'HomeController@index');
// Зарегистрированные пользователи - мидлвар «auth»
Route::group(['middleware' => ['auth']], function () {
// /user/XXX: В дополнении к «auth», эта группа использует мидлвар «simple_users»
Route::group(['middleware' => ['simple_users'], 'prefix' => 'user'], function () {
Route::resource('tasks', 'TaskController');
});
// /admin/XXX: В этой группе не будет «simple_users», но будут «auth» и «admins»
Route::group(['middleware' => ['admins'], 'prefix' => 'admin'], function () {
Route::resource('users', 'UserController');
});
});
Совет 3. Валидация параметров маршрута — мультиязычный пример
Типичный случай — префикс маршрутов, зависящий от локали, например, fr/blog и en/article/333. Как мы можем гарантировать, что две первые буквы будут использоваться только для переключения языка? А мы можем их проверить прямо в маршруте, с помощью параметра where:
Route::group(['prefix' => '{locale}', 'where' => ['locale' => '[a-zA-Z]{2}']], function () {
Route::get('/', 'HomeController@index');
Route::get('article/{id}', 'ArticleController@show');
});
Основная часть — 'where' => ['locale' => '[a-zA-Z]{2}'], где мы используем регулярное выражение для сопоставления только двухбуквенных комбинаций.
Совет 4. Динамическая маршрутизация субдомены
Это есть в официальной документации Laravel, но используется редко, поэтому я решил рассказать об этом.
Если у вас есть динамические субдомены, например, отдельный для каждого пользователя, то он должен быть переменной, верно? Laravel делает это автоматически. Смотрим пример:
Route::domain('{account}.myapp.com')->group(function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
Обратите внимание, что {account} автоматически передается как параметр $account во все методы контроллера, поэтому не забудьте её принять в каждом их них.
Совет 5. Будьте осторожны с неанглийской привязкой модели маршрута
Иногда URL-адреса должны содержать неанглийские слова. Например, у вас есть портал для книг на испанском языке, и вы хотите, чтобы у списка книг был URL /libros, а у отдельной книги — /libros/1, как в обычном ресурсном контроллере.
Но в базе данных все имена должны быть на английском языке, чтобы «магия» Laravel могла работать с единственным и множественным числом, верно?
И, когда вы создаете модель Book с Миграцией и Контроллером, то используете эту команду:
php artisan make:model Book -mcr
Ключ -mcr генерирует Модель и ресурсный Контроллер. В этом контроллере у вас будет:
/**
* Display the specified resource.
*
* @param \App\Book $book
* @return \Illuminate\Http\Response
*/
public function show(Book $book)
{
// ...
}
Но в вашем routes/web.php у вас сделано так:
Route::resource('libros', 'BookController');
Проблема в том, что это не заработает. Еще большая проблема в том, что не выдаст никакой ошибки, просто $book будет пустым, и вы не поймете, почему.
Согласно официальному описанию контроллера ресурсов, имя переменной должно совпадать с параметром в единственном числе:
// Поэтому вместо
public function show(Book $book)
{
// ...
}
// нужно сделать
public function show(Book $libro)
{
// ...
}
Но, честно говоря, в неанглийских проектах я бы рекомендовал вообще не использовать Route::resource и Привязку Модели к Маршруту.
«Магия» слишком непредсказуема. Например, как бы Laravel догадался бы, что единственное число от «libros» — это «libro»?
Совет 6. Маршруты API — из V1 в V2
Представьте, что вы работаете с API-проектом, и вам необходимо выпустить его новую версию. Старые конечные точки останутся по адресу api/[что-то-там], а для новой версии вы будете использовать api/V2/[что-то-там].
Вся логика app/Providers/RouteServiceProvider.php:
public function map()
{
$this->mapApiRoutes();
$this->mapWebRoutes();
// ...
}
protected function mapWebRoutes()
{
Route::middleware('web')
->namespace($this->namespace)
->group(base_path('routes/web.php'));
}
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
Как видите, API-маршруты регистрируются в отдельной функции с префиксом api/.
Итак, если вы хотите создать группу маршрутов V2, то вы можете создать отдельный файл routes/api_v2.php и сделать так:
public function map()
{
// ... старый функции
$this->mapApiV2Routes();
}
// И новая функция
protected function mapApiV2Routes()
{
Route::prefix('api/V2')
->middleware('api')
->namespace($this->namespace)
->group(base_path('routes/api_v2.php'));
}
Таким образом, и старые маршруты не поломаются, и вы легко создадите новый набор маршрутов.
Совет 7. Ограничение скорости — глобальное и для Гостей/Пользователей
Это также есть в официальной документации, но не так подробно.
Во-первых, вы можете ограничить некоторые URL-адреса для вызова максимум 60 раз в минуту, с помощью throttle:60,1.
Route::middleware('auth:api', 'throttle:60,1')->group(function () {
Route::get('/user', function () {
//
});
});
А вы знали, что можно это сделать отдельно для гостей и залогиненных пользователей?
// максимум 10 запросов в минуту для гостей и 60 для пользователей
Route::middleware('throttle:10|60,1')->group(function () {
//
});
Также вы можете создать в БД поле users.rate_limit и ограничить запросы для конкретного пользователя:
Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () {
Route::get('/user', function () {
//
});
});
Совет 8. Список Маршрутов и Кеширование Маршрутов
Последний совет — как проверить существующие маршруты.
Не все знают, какие именно маршруты скрыты в Route::resource() или в более сложной Route::group.
Но в любой момент вы можете это узнать с помощью artisan-команды:
php artisan route:list
Имейте в виду, что если вы используете кэширование маршрутов, то после каждого изменения маршрутов вам нужно запускать команду:
php artisan route:clear
Автор: Povilas Korop
Перевод: Алексей Широков
Наш Телеграм-канал — следите за новостями о Laravel.
