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.dashboard.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'],
        ]);
    }
}

 

На цьому на сьогодні все. Успіхів!