Laravel: Управление маршрутами в большом приложении

Источник: «Managing Routes in a large Laravel application»
Файлы маршрутов 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 групп, это всё ещё читабельно.

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

А как вы боретесь с когнитивной нагрузкой большого файла маршрутов? У вас есть интересный способ, которым вы хотите поделиться? Напишите о нём.

Дополнительные материалы

Предыдущая Статья

Как установить PHP 8.2 на Debian/Ubuntu

Следующая Статья

Laravel: Data Transfer Objects — Зачем и Как