Laravel: Управление маршрутами в большом приложении
Вы можете использовать разные подходы, в зависимости от того, какой предпочитаете подход. В этом руководстве я начну рассматривать несколько вариантов, которые я видел. А закончу тем, как я подхожу к этой проблеме и почему именно так.
Я приведу простой пример, но вы можете немного проявить своё воображение! Представьте, что мы создаём приложение API позволяющее клиентам делать заказы онлайн из каталога и позволяет клиентам отслеживать их поставки.
Route Service Provider
Используя route service provider, вы можете легко добавлять дополнительные записи маршрутов. Давайте рассмотрим пример:
class RouteServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->configureRateLimiting();
$this->routes(function (): void {
Route::middleware('api')
->group(base_path('routes/api.php'));
});
}
}
Он похож на RouteServiceProvider
по умолчанию, который вы получаете в своём Laravel проекте. Ваши могут отличаться в зависимости от возраста вашего приложения. Как его расширить? Мы можем добавить дополнительную загрузку маршрута в провайдере:
class RouteServiceProvider extends ServiceProvider
{
public function boot(): void
{
$this->configureRateLimiting();
$this->routes(function (): void {
Route::middleware('api')
->group(base_path('routes/api.php'));
Route::middleware('api')
->group(base_path('routes/resource/catalog.php'));
Route::middleware('api')
->group(base_path('routes/resource/orders.php'));
Route::middleware('api')
->group(base_path('routes/resource/payments.php'));
Route::middleware('api')
->group(base_path('routes/resource/deliveries.php'));
});
}
}
Этот пример не является идеально точным. Я использую его больше как пример с несколькими движущимися частями, которые, вероятно, имеют не менее 75+ маршрутов. Итак, в этом случае мы управляем маршрутами в RouteServiceProvider
, поэтому всё играет важную роль в том, как мы загружаем маршруты. Самая большая проблема обнаруженная мною при таком подходе, заключается в том, что в файле маршрутов нужно знать, что ещё загружается. Ранее я работал над очень большим приложением, в котором использовался такой подход. Мне требовалось много умственных усилий, чтобы постоянно возвращаться и проверять загружаются ли маршруты и не находятся ли они в порядке, который может вызвать проблемы.
Открывая проект Laravel в первый раз, вы обращаете внимание на три ключевых области: Модели Eloquent, Маршруты и Тесты. Вы открываете файл маршрутов и просматриваете зарегистрированные маршруты, чтобы понять размер приложения и то, как всё устроено.
Подключение файлов
Если вы установите Laravel Breeze, то заметите, что он добавляет следующее в файл routes/web.php
:
require __DIR__ . '/auth.php';
Это загружаются маршруты аутентификации поставляемые с пакетом, позволяя сделать ваш Service Provider меньше и понять, сколько дополнительных файлов необходимо проверить для просмотра всех маршрутов. Давайте возьмём приведённый выше пример и добавим маршруты используя этот подход:
// Остальная часть вашего файла `routes/api.php`
require __DIR__ . '/resource/catalog.php';
require __DIR__ . '/resource/orders.php';
require __DIR__ . '/resource/payments.php';
require __DIR__ . '/resource/deliveries.php';
На мой взгляд, это улучшение подхода к Service Provider, поскольку она более наглядно показывает маршруты используемые вашим приложением. Однако подключение файлов — это то, что я ненавижу. Это просто кажется грязным и ленивым.
Преимущество заключается в том, что вы можете увидеть всё необходимое для загрузки всех маршрутов в одном простом месте, где вы ожидаете увидеть маршруты. Недостаток в том, что это всего лишь метод подключения, который загружает файл при выполнении этого скрипта.
На мой взгляд это незначительное улучшение. Здесь нет информации о том, на что может быть похожа структура адресов, или какое дополнительное middleware применяется к маршрутам.
Группы Маршрутов
Это предпочитаемый мной подход. Мы видим, что он используется в RouteServiceProvider
, откуда я взял эту идею. Основной принцип заключается в том, что в вашем основном файле маршрутов (в нашем случае это — routes/api.php
), мы создаём группы маршрутов, как если бы добавляли маршруты вручную, а затем сообщаем этой группе, что нужно использовать отдельный файл.
// Остальная часть вашего файла `routes/api.php`
Route::prefix('catalog')->as('catalog:')->middleware(['auth:sanctum'])->group(
base_path('routes/resources/catalog.php'),
);
Route::prefix('orders')->as('orders:')->middleware(['auth:sanctum'])->group(
base_path('routes/resources/orders.php'),
);
Route::prefix('payments')->as('payments:')->middleware(['auth:sanctum'])->group(
base_path('routes/resources/payments.php'),
);
Route::prefix('deliveries')->as('deliveries:')->middleware(['auth:sanctum'])->group(
base_path('routes/resources/deliveries.php'),
);
Как видно из вышеизложенного, мы просто используем хэлпер base_path
для загрузки маршрутов в нужном месте. Глядя на файл маршрутов, мы видим, что группы создают приложение, но они не занимают весь файл — даже если у вас будет 20-30 групп, это всё ещё читабельно.
Отсюда мы можем управлять ресурсами
и под-ресурсами
в выделенном файле маршрутов, что означает меньшее количество имён классов во время импорта и наличие выделенных файлов, которые можно легко понять изолированно или в содержимом всего вашего приложения.
А как вы боретесь с когнитивной нагрузкой большого файла маршрутов? У вас есть интересный способ, которым вы хотите поделиться? Напишите о нём.