Laravel 8: как удобно разделить роуты?

Laravel 8: как удобно разделить роуты?

Ubuntu: 20.04
PHP: 8.0.8
Laravel: 8.49.1

Начнём с того, что даже в небольших приложениях скорее всего будет админка, и ладно, если для пользовательской части используется SPA, т.е. роуты для неё находятся в файле api.php. В этом случае маршруты админ-панели как и обычно могут распологатся в web.php. Если же для всего приложения используется сугубо Laravel, то лучше вынести админ-роуты в отдельный файл. 

Исходим из того, что мы хотим:

  • чтобы у всех маршрутов админки был префикс admin. Например: my-app.com/admin/login, my-app.com/admin/customers и т.д.
  • чтобы имя таких роутов также содержало префикс admin, т.е. мы могли вызывать роуты в приложении как route('admin.dashboard') или route('admin.customers.index') и т.д.

Предположим, что файл с машрутизацией будет называться admin.php. Идём в файл app/Providers/RouteServiceProvider.php, и редактируем метод boot():

<?php

namespace App\Providers;

...

class RouteServiceProvider extends ServiceProvider
{
    ...

    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            Route::prefix('api')
                ->middleware('api')
                ->namespace($this->namespace)
                ->group(base_path('routes/api.php'));

            Route::middleware('web')
                ->namespace($this->namespace)
                ->group(base_path('routes/web.php'));

            // Admin routes
            Route::prefix('admin')
                ->name('admin.')
                ->middleware('web')
                ->group(base_path('routes/admin.php'));
        });
    }

    ...
}

 

В директории routes создадим файл admin.php:

<?php

use App\Http\Livewire\Admin\Login;
use App\Http\Livewire\Admin\CustomersList;
use App\Http\Livewire\Admin\Dashboard;

/** Admin routes examples */
Route::group(['middleware' => 'guest:admin'], function () {
    Route::get('login', Login::class)->name('login');
});

Route::group(['middleware' => 'auth:admin'], function () {
    Route::get('/', Dashboard::class)->name('dashboard');
    Route::get('customers', CustomersList::class)->name('customers.index');
    ...
});

Примечание: предполагается, что у Вас есть middleware для проверки того, является ли пользователь админом. Также для примера в роутах используются компоненты Livewire, но всё то же самое справедливо и для контроллеров. 

 

Теперь о средних и больших приложениях. Предположим у нас есть medium-size на Laravel, т.е. нам нужны web-маршруты для конечного пользователя и админ-панели. Кроме этого, есть мобильные приложения, для которых у нас есть API. Вероятнее всего, разделения на пару отдельных файлов бдует недостаточно. Лучше создать директории, каждая из которых будет содержать свои файлы. Структура директорий/файлов может выглядеть следующим образом:

my-app
  ...
  routes
    admin
      customers.php
      products.php
      ...
    api
      customers.php
      products.php
      ...
    web
      customers.php
      products.php
      ...
  ...

 

Помним о том, что каждой группе роутов может понадобится префикс к маршруту и имени, а также middleware. Снова идём в RouteServiceProvider, добавим параметры групп:

private function getRouteParameters(): Collection
{
    return collect([
        'admin' => ['dir' => 'admin', 'prefix' => 'admin', 'name' => 'admin.', 'middleware' => 'web'],
        'api' => ['dir' => 'api', 'prefix' => 'api', 'name' => 'api.', 'middleware' => 'api'],
        'web' => ['dir' => 'web', 'prefix' => '', 'name' => '', 'middleware' => 'web'],
    ]);
}

 

Также нужен будет метод который будет обрабатывать файлы маршрутов отдельной директории:

private function mapRoutes($params)
{
    collect(File::files(base_path('routes/'.$params['dir'])))->each(function ($file) use ($params) {
        Route::prefix($params['prefix'])
            ->name($params['name'])
            ->middleware($params['middleware'])
            ->namespace($this->namespace)
            ->group($file);
    });
}

 

...и завершающий шаг - модификация метода boot():

public function boot()
{
    $this->configureRateLimiting();

    $this->routes(function () {
        $this->getRouteParameters()->each(function ($params) {
            $this->mapRoutes($params);
        });
    });
}

 

В конечно итоге наш app/Providers/RouteServiceProvider.php будет выглядеть так:

<?php

namespace App\Providers;

use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    public const HOME = '/dashboard';

    public function boot()
    {
        $this->configureRateLimiting();

        $this->routes(function () {
            $this->getRouteParameters()->each(function ($params) {
                $this->mapRoutes($params);
            });
        });
    }

    protected function configureRateLimiting()
    {
        RateLimiter::for('api', function (Request $request) {
            return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip());
        });
    }

    private function mapRoutes($params)
    {
        collect(File::files(base_path('routes/'.$params['dir'])))->each(function ($file) use ($params) {
            Route::prefix($params['prefix'])
                ->name($params['name'])
                ->middleware($params['middleware'])
                ->namespace($this->namespace)
                ->group($file);
        });
    }

    private function getRouteParameters(): Collection
    {
        return collect([
            'admin' => ['dir' => 'admin', 'prefix' => 'admin', 'name' => 'admin.', 'middleware' => 'web'],
            'api' => ['dir' => 'api', 'prefix' => 'api', 'name' => 'api.', 'middleware' => 'api'],
            'web' => ['dir' => 'web', 'prefix' => '', 'name' => '', 'middleware' => 'web'],
        ]);
    }
}

 

На этом на сегодня всё. Успехов!