Представьте себе проект Laravel с более чем 100 маршрутами, отдельным разделом для гостей, пользователей, администраторов и т. д. Вы действительно хотите хранить все это в одном файле? Как их сгруппировать, добавить префиксы к URL? Давайте посмотрим, какие у нас есть варианты.

1. Отдельные маршруты WEB и API

Это легко, так как Laravel поставляется с этой функцией по умолчанию. Есть два файла:

Поэтому, если в вашем проекте есть как визуальные веб-страницы, так и API (что в наши дни встречается все чаще и чаще), поместите маршруты API в этот отдельный файл.

Например, если у вас есть страница /users, а затем конечная точка /api/users/, разделение их на отдельные файлы поможет избежать путаницы с одинаковыми именами в одном и том же файле.

Тем не менее, я недавно видел контр-интуитивный пример из официального проекта Laravel. В Laravel Horizon у Тейлора есть только маршруты API, и он не использовал отдельный файл, а поместил его в route/web.php :

Screen-Shot-2019-06-22-at-7.35.16-PM-1068x669
 Еще одно доказательство того, что структурирование в Laravel очень личное и нет 100% стандарта, даже от самого Тейлора.


2. Структурируйте файл route/web.php по группам

Это также происходит из «базового» Laravel — группировки маршрутов. Это пример из официальной документации Laravel:

Route::middleware(['first', 'second'])->group(function () {
    Route::get('/', function () {
        // Uses first & second Middleware
    });

    Route::get('user/profile', function () {
        // Uses first & second Middleware
    });
});

Самое простое использование — скрытие разных групп под разным промежуточным ПО. Например, вы хотите, чтобы одна группа была ограничена промежуточным программным обеспечением аутентификации по умолчанию, другая группа — отдельным промежуточным программным обеспечением администратора и т. д.

При этом вы также можете использовать имена и префиксы групп маршрутов. Опять же, несколько примеров из официальной документации:

Route::prefix('admin')->group(function () {
    Route::get('users', function () {
        // Matches The "/admin/users" URL
    });
});

Route::name('admin.')->group(function () {
    Route::get('users', function () {
        // Route assigned name "admin.users"...
    })->name('users');
});

Кроме того, если вы хотите добавить все промежуточное ПО + имя + префикс в одну группу, более читабельно поместить их в массив:

// Instead of chaining like this: 
Route::name('admin.')->prefix('admin')->middleware('admin')->group(function () {
    // ...
});

// You can use an array
Route::group([
    'name' => 'admin.', 
    'prefix' => 'admin', 
    'middleware' => 'auth'
], function () {
    // ...
});

Давайте свяжем все это вместе с реальным примером и тремя группами:

  • Группа «Гость» с URL-адресами /front/XXXXX и без промежуточного ПО;
  • Группа «Пользователь» с URL-адресами /user/XXXXX и промежуточным ПО для аутентификации;
  • Группа «Администратор» с URL-адресами /admin/XXXXX и пользовательским промежуточным ПО для администрирования.

Вот способ сгруппировать все это в файле route/web.php:

Route::group([
    'name' => 'admin.',
    'prefix' => 'admin',
    'middleware' => 'admin'
], function () {

    // URL: /admin/users
    // Route name: admin.users
    Route::get('users', function () {
        return 'Admin: user list';
    })->name('users');

});

Route::group([
    'name' => 'user.',
    'prefix' => 'user',
    'middleware' => 'auth'
], function () {

    // URL: /user/profile
    // Route name: user.profile
    Route::get('profile', function () {
        return 'User profile';
    })->name('profile');

});

Route::group([
    'name' => 'front.',
    'prefix' => 'front'
], function () {

    // No middleware here
    // URL: /front/about-us
    // Route name: front.about
    Route::get('about-us', function () {
        return 'About us page';
    })->name('about');

});

3. Группировка контроллеров с помощью пространств имен

В приведенном выше примере мы не использовали контроллеры, мы просто вернули статический текст в качестве примера. Давайте добавим контроллеры, но с еще одной «изюминкой» — мы будем структурировать их по папкам с разными пространствами имен, например так:

Screen-Shot-2019-06-23-at-10.16.05-AM
И затем мы можем использовать их в нашем файле Routes:

Route::group([
    'name' => 'front.',
    'prefix' => 'front'
], function () {
    Route::get('about-us', 'Front\AboutController@index')->name('about');
});

Но что, если у нас много контроллеров в этой группе? Должны ли мы постоянно добавлять Front\SomeController? Конечно нет. Вы также можете указать пространство имен в качестве одного из параметров.

Route::group([
    'name' => 'front.',
    'prefix' => 'front',
    'namespace' => 'Front',
], function () {
    Route::get('about-us', 'AboutController@index')->name('about');
    Route::get('contact', 'ContactController@index')->name('contact');
});

4. Группа внутри группы

Ситуация выше с тремя группами упрощена, реальные проекты имеют немного другую структуру — из двух групп: front и auth. А дальше внутри auth есть подгруппы: user и admin. Для этого мы можем создать подгруппы в route/web.php и назначить различные промежуточные программы/префиксы и т. д.

Route::group([
    'middleware' => 'auth',
], function() {

    Route::group([
        'name' => 'admin.',
        'prefix' => 'admin',
        'middleware' => 'admin'
    ], function () {

        // URL: /admin/users
        // Route name: admin.users
        Route::get('users', 'UserController@index')->name('users');

    });

    Route::group([
        'name' => 'user.',
        'prefix' => 'user',
    ], function () {

        // URL: /user/profile
        // Route name: user.profile
        Route::get('profile', 'ProfileController@index')->name('profile');

    });

});

Мы можем сделать это даже с более чем двумя уровнями, вот пример из проекта с открытым исходным кодом Akaunting:

Route::group(['middleware' => 'language'], function () {
    Route::group(['middleware' => 'auth'], function () {
        Route::group(['prefix' => 'uploads'], function () {
            Route::get('{id}', 'Common\Uploads@get');
            Route::get('{id}/show', 'Common\Uploads@show');
            Route::get('{id}/download', 'Common\Uploads@download');
        });

        Route::group(['middleware' => 'permission:read-admin-panel'], function () {
            Route::group(['prefix' => 'wizard'], function () {
                Route::get('/', 'Wizard\Companies@edit')->name('wizard.index');
        
        // ...

Другой пример — из другой популярной CRM Laravel под названием Monica :

Route::middleware(['auth', 'verified', 'mfa'])->group(function () {
    Route::name('dashboard.')->group(function () {
        Route::get('/dashboard', 'DashboardController@index')->name('index');
        Route::get('/dashboard/calls', 'DashboardController@calls');
        Route::get('/dashboard/notes', 'DashboardController@notes');
        Route::get('/dashboard/debts', 'DashboardController@debts');
        Route::get('/dashboard/tasks', 'DashboardController@tasks');
        Route::post('/dashboard/setTab', 'DashboardController@setTab');
    });

5. Глобальные настройки в RouteServiceProvider

Есть файл, который служит для всех настроек маршрутов — app/Providers/RouteServiceProvider.php. У него есть метод map(), где он связывает оба файла маршрутов — веб и API:

    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, поскольку их настройки обычно одинаковы, например:

protected function mapApiRoutes()
{
    Route::group([
        'middleware' => ['api'],
        'namespace' => $this->namespace,
        'prefix' => 'api/v1',
    ], function ($router) {
        require base_path('routes/api.php');
    });
}

Этот вышеприведенный метод будет ставить перед всеми URL-адресами API префикс api/v1/ в начале.


6. Группировка в дополнительные файлы — стоит ли?

Если у вас огромное количество маршрутов и вы хотите сгруппировать их еще больше, в отдельные файлы, то вы можете использовать тот же файл, упомянутый в предыдущем разделе — app/Providers/RouteServiceProvider.php. Если вы внимательно посмотрите на его методы map(), вы увидите закомментированное место в конце:

public function map()
{
    $this->mapApiRoutes();

    $this->mapWebRoutes();

    //
}

Вы можете интерпретировать это как своего рода «приглашение» добавить больше файлов, если хотите. Таким образом, вы можете создать другой метод, такой как mapAdminRoutes() внутри этого файла, а затем добавить его в метод map(), и ваш отдельный файл будет зарегистрирован и загружен автоматически.

Но лично я не вижу особых преимуществ в этом подходе и нечасто встречал его. Это дает немного больше разделения маршрутов, но иногда вы теряетесь в этих файлах и не знаете, где искать конкретный маршрут.


7. Найдите точный маршрут с помощью команды Artisan route:list

Говоря о больших маршрутах и ​​заблудиться там, у нас есть одна команда-ремесленник, которая помогает найти определенный маршрут.

Вы, наверное, все знаете, что php artisan route:list выдаст вам все маршруты в проекте.

Но знаете ли вы, что у вас есть больше возможностей фильтрации, чтобы найти именно то, что вы хотите? Просто добавьте --method, или --name, или --path с параметрами.

Фильтровать по методу — GET, POST и т. д.

Фильтровать по имени или части URL.

Это все, что я мог рассказать о группировке маршрутов в больших проектах. У вас есть другие примеры? Пожалуйста, поделитесь в комментариях.