2.

Laravelルートグループ|prefix・middleware・nameで一括管理

編集
この記事の要点
  • Route::group() や流暢な修飾メソッド(prefix / middleware / name / namespace / domain)でルートを一括管理できる
  • prefix('admin') で URL に共通の接頭辞、name('admin.') でルート名にも接頭辞を付与
  • middleware(['auth']) で認証必須エリア、controller(UserController::class) で同一コントローラを束ねる
  • グループは入れ子可能。外側と内側の prefix / middleware / name は連結される
  • API バージョニング・管理画面・認証必須エリア・サブドメインのいずれも、グループで宣言的に表現できる

ルートグループとは

Laravel の Route::group() は、複数のルートに共通する prefix / middleware / name / namespace / domain をまとめて指定できる仕組みです。共通項を一カ所で宣言できるので、コードの重複を大きく減らせます。

最もシンプルな例

use Illuminate\Support\Facades\Route;

Route::prefix('article')->group(function () {
    Route::get('',     [ArticleController::class, 'index']);   // /article
    Route::get('old',  [ArticleController::class, 'getOld']);  // /article/old
    Route::get('diff', [ArticleController::class, 'getDiff']); // /article/diff
});

3 つのルートすべてに /article が付き、URL が共通します。

主な修飾メソッド

メソッド役割
prefix()URL の接頭辞admin → /admin/...
middleware()ミドルウェア["auth", "verified"]
name()ルート名の接頭辞admin. → admin.users.index
controller()同一コントローラを束ねるUserController::class
domain()サブドメイン{tenant}.example.com
namespace()コントローラの名前空間App\Http\Controllers\Admin(L8 以降は非推奨)
withoutMiddleware()個別にミドルウェア除外[VerifyCsrfToken::class]

prefix(URL 接頭辞)

Route::prefix('api/v1')->group(function () {
    Route::get('users',      [UserController::class, 'index']);   // /api/v1/users
    Route::get('users/{id}', [UserController::class, 'show']);    // /api/v1/users/{id}
    Route::post('users',     [UserController::class, 'store']);   // /api/v1/users
});

middleware(共通の認証・権限)

Route::middleware(['auth', 'verified'])->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard');
    Route::get('/profile',   [ProfileController::class, 'edit'])->name('profile.edit');
});

// 管理者専用
Route::middleware(['auth', 'can:admin'])->prefix('admin')->name('admin.')->group(function () {
    Route::get('users', [Admin\UserController::class, 'index'])->name('users.index');
    Route::get('logs',  [Admin\LogController::class,  'index'])->name('logs.index');
});

name(ルート名接頭辞)

name() はルート名にドット区切りで接頭辞を付けます。route(\'admin.users.index\') で参照できます。

Route::name('admin.')->prefix('admin')->group(function () {
    Route::get('users', [Admin\UserController::class, 'index'])->name('users.index');
    // ルート名: admin.users.index
});

controller(同じコントローラを束ねる)

Laravel 8 から、同じコントローラを使うルートを controller() でまとめられます。配列の代わりにメソッド名だけ書けるのですっきりします。

Route::controller(UserController::class)->prefix('users')->group(function () {
    Route::get('',         'index')->name('users.index');
    Route::get('{id}',     'show')->name('users.show');
    Route::post('',        'store')->name('users.store');
    Route::put('{id}',     'update')->name('users.update');
    Route::delete('{id}',  'destroy')->name('users.destroy');
});

domain(サブドメイン)

マルチテナント SaaS でよく使うパターン。

Route::domain('{tenant}.example.com')->group(function () {
    Route::get('/', function (string $tenant) {
        return "Tenant: $tenant";
    });
});

グループの入れ子

グループは入れ子にできます。外側と内側の prefix / middleware / name はすべて連結されます。

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

    Route::get('dashboard', [DashboardController::class, 'index'])->name('dashboard');
    //   middleware: auth
    //   URL:        /admin/dashboard
    //   name:       admin.dashboard

    Route::middleware(['can:logs'])->prefix('logs')->name('logs.')->group(function () {
        Route::get('',       [LogController::class, 'index'])->name('index');
        // middleware: auth, can:logs
        // URL:        /admin/logs
        // name:       admin.logs.index
    });
});

API バージョニングの実例

Route::prefix('api')->middleware('api')->group(function () {

    Route::prefix('v1')->name('api.v1.')->group(function () {
        Route::apiResource('users',    Api\V1\UserController::class);
        Route::apiResource('articles', Api\V1\ArticleController::class);
    });

    Route::prefix('v2')->name('api.v2.')->group(function () {
        Route::apiResource('users',    Api\V2\UserController::class);
        Route::apiResource('articles', Api\V2\ArticleController::class);
    });
});

認証エリアの典型構成

// ゲスト
Route::middleware('guest')->group(function () {
    Route::get('login',     [AuthController::class, 'showLogin'])->name('login');
    Route::post('login',    [AuthController::class, 'login']);
    Route::get('register',  [AuthController::class, 'showRegister'])->name('register');
});

// ログイン済みユーザ
Route::middleware('auth')->group(function () {
    Route::post('logout', [AuthController::class, 'logout'])->name('logout');

    Route::prefix('mypage')->name('mypage.')->group(function () {
        Route::get('',      [MypageController::class, 'index'])->name('index');
        Route::get('edit',  [MypageController::class, 'edit'])->name('edit');
    });
});

withoutMiddleware で個別除外

大きなグループの一部だけミドルウェアを外したいときに使います。

Route::middleware('auth')->group(function () {
    Route::get('/dashboard', [DashboardController::class, 'index']);

    // この 1 つだけ auth を外したい(公開コールバック等)
    Route::post('/webhook', [WebhookController::class, 'handle'])
        ->withoutMiddleware('auth');
});

route:list で確認

定義したグループが意図通りに展開されているかは、Artisan コマンドで確認します。

php artisan route:list
php artisan route:list --name=admin   # 名前フィルタ
php artisan route:list --path=admin   # パスフィルタ

よくあるトラブル

症状原因と対処
名前付きルートが見つからない外側で name() を付けるのを忘れている。php artisan route:list --name=... で確認
middleware が効かないapp/Http/Kernel.php に登録漏れ / グループ外で個別 middleware が上書きしている
prefix が二重に付く外側 + 内側で同じ prefix を指定している。入れ子の重複に注意
controller() を使ってもメソッドが見つからない古い Laravel(< 8)。配列形式 [Class::class, "method"] を使う
サブドメインルートが効かないキャッシュ削除(route:clear)/ Web サーバのワイルドカード DNS / 仮想ホスト設定を確認

関連

  • ルーティング — 親カテゴリ
  • Route::resource — リソースコントローラ
  • middleware — ミドルウェア
  • route() ヘルパ — ルート名から URL 生成
  • route:cache / route:list — キャッシュ・一覧
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. ルーティング一覧の確認方法
  2. ルーティングのグループ化

最近更新/作成されたページ