【Laravel】Middlewareとは?認証チェックなどの共通処理を挟む仕組み

laravelアイキャッチ Laravel

こんにちは、かつコーチです。

前回はControllerの作り方を解説しました。

RouteとControllerでリクエストの処理ができるようになると、次に気になってくるのが「ログインしていない人にはこのページを見せたくない」といった共通のチェック処理です。

毎回Controllerの中に同じチェックを書くのは非効率ですし、書き忘れも起きやすくなります。

そこで登場するのがMiddlewareです。

Middlewareとは?

リクエストとレスポンスの間に挟む処理

Middleware(ミドルウェア)とは、リクエストがControllerに届く前、またはレスポンスがブラウザに返る前に、共通の処理を挟み込む仕組みです。

イメージとしては、空港の保安検査場に近いものだと考えると分かりやすいかもしれません。

飛行機(Controller)に乗る前に、必ず保安検査(Middleware)を通過する必要があり、検査に引っかかった人はそこで搭乗できずに止められます。

Laravelのリクエストも同じで、次のような流れになります。

ブラウザからのリクエスト
   ↓
Middleware(ここでチェック)
   ↓
Controller(チェックを通過した場合のみ到達)
   ↓
レスポンスを返す

代表的な用途は次のようなものです。

  • ログインしていないユーザーをログインページにリダイレクトする(認証チェック)
  • 管理者権限を持たないユーザーのアクセスを拒否する(権限チェック)
  • リクエストのログを記録する
  • メンテナンス中はすべてのアクセスをメンテナンスページに誘導する

なぜControllerに直接書かないのか

「チェック処理くらいControllerに書けばいいのでは」と思うかもしれませんが、それをやると次のような問題が起きます。

❌ Before:Controllerの中に認証チェックを毎回書く

<?php

namespace App\Http\Controllers;

use Illuminate\Support\Facades\Auth;

class MypageController extends Controller
{
    public function index()
    {
        if (!Auth::check()) {
            return redirect('/login');
        }

        return view('mypage.index');
    }
}

class ArticleController extends Controller
{
    public function create()
    {
        if (!Auth::check()) {
            return redirect('/login');
        }

        return view('articles.create');
    }
}

ログインが必要なページが増えるたびに、同じ4行を毎回コピー&ペーストすることになります。

チェックの条件を変更したくなったとき、すべてのControllerを1つずつ書き換える必要があり、修正漏れが起きやすくなります。

✅ After:Middlewareに認証チェックをまとめる

<?php
// routes/web.php

use App\Http\Controllers\ArticleController;
use App\Http\Controllers\MypageController;
use Illuminate\Support\Facades\Route;

Route::middleware('auth')->group(function () {
    Route::get('/mypage', [MypageController::class, 'index']);
    Route::get('/articles/create', [ArticleController::class, 'create']);
});
<?php

namespace App\Http\Controllers;

class MypageController extends Controller
{
    public function index()
    {
        // 認証チェックはMiddlewareが済ませてくれているので、ここには書かない
        return view('mypage.index');
    }
}

認証チェックのロジックが1か所(Middleware)にまとまるため、条件を変えたくなったときの修正箇所も1か所で済みます。

Controller側は「本来やりたい処理」だけに集中できるようになり、コードの見通しも良くなります。

LaravelにあらかじめMiddlewareが用意されている

auth Middleware

Laravelには、よく使うMiddlewareがあらかじめ用意されています。

代表的なのが auth Middlewareで、これを指定したルートは、ログインしていないユーザーがアクセスすると自動的にログインページへリダイレクトされます。

<?php

use Illuminate\Support\Facades\Route;

Route::get('/mypage', function () {
    return 'ログイン済みの人だけが見られるページです';
})->middleware('auth');

前回の記事で紹介したグループ化と組み合わせれば、複数のルートにまとめて適用できます。

<?php

use Illuminate\Support\Facades\Route;

Route::middleware('auth')->group(function () {
    Route::get('/mypage', function () {
        //
    });

    Route::get('/settings', function () {
        //
    });
});

自分でMiddlewareを作る

Artisanコマンドで生成する

独自のチェック処理を作りたいときは、Artisanコマンドで生成します。

php artisan make:middleware CheckAdmin

app/Http/Middleware/CheckAdmin.php が生成され、中身は次のような形になっています。

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckAdmin
{
    public function handle(Request $request, Closure $next): Response
    {
        return $next($request);
    }
}

管理者権限チェックの実装例

このひな形に、管理者チェックの処理を追加してみましょう。

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class CheckAdmin
{
    public function handle(Request $request, Closure $next): Response
    {
        if ($request->user() === null || !$request->user()->is_admin) {
            abort(403, '管理者権限が必要です');
        }

        return $next($request);
    }
}

ポイントは $next($request) の部分です。

  • チェックに引っかかった場合:$next($request) を呼ばずに abort() などで処理を止める
  • チェックを通過した場合:$next($request) を呼び出し、後続の処理(次のMiddlewareやController)に進める

「次の処理に進めるかどうかのゲート」というイメージを持つと理解しやすいです。

作ったMiddlewareを登録して使う

作成したMiddlewareは、bootstrap/app.php(Laravel 11以降の場合)でエイリアス(短い呼び名)を登録してから使います。

<?php
// bootstrap/app.php

use App\Http\Middleware\CheckAdmin;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

return Application::configure(basePath: dirname(__DIR__))
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->alias([
            'admin' => CheckAdmin::class,
        ]);
    })
    ->create();

登録さえ済ませれば、他のMiddlewareと同じように middleware('admin') の形で使えます。

<?php

use Illuminate\Support\Facades\Route;

Route::get('/admin/dashboard', function () {
    return '管理画面です';
})->middleware(['auth', 'admin']);

middleware(['auth', 'admin']) のように配列で複数指定すれば、「ログイン済み、かつ管理者である」という2段階のチェックを順番に適用できます。

つまずきやすいポイント:Middlewareの適用順序

配列の順番が処理順になる

私が実際にハマったのが、Middlewareを複数指定するときの順番です。

❌ Before:チェックの順番を意識せずに並べる

<?php

Route::get('/admin/dashboard', function () {
    return '管理画面です';
})->middleware(['admin', 'auth']);

一見動きそうに見えますが、admin Middlewareが先に実行されるため、「そもそもログインしているかどうか」が確認される前に、$request->user() を呼び出して権限チェックをすることになります。

未ログイン状態では $request->user()null になるため、想定通りに動くこともありますが、チェックの意図(まずログイン確認、それから権限確認)とコードの見た目が一致しておらず、後から読んだときに誤解を招きやすい書き方です。

✅ After:チェックしたい順番に並べる

<?php

Route::get('/admin/dashboard', function () {
    return '管理画面です';
})->middleware(['auth', 'admin']);

配列に指定したMiddlewareは、左から順番に実行されます。

「まずログインしているか確認し、次に管理者かどうかを確認する」という、実際にチェックしたい順序と配列の並び順を一致させておくことで、コードを読んだときの意図も明確になります。

私はこの順序を意識せずにMiddlewareを並べていて、権限チェックのロジックが複雑になったときに処理の流れを追えなくなった経験があります。

複数のMiddlewareを組み合わせるときは、「どちらを先にチェックすべきか」を常に意識するようにしましょう。

まとめ

この記事のポイント

  • Middlewareは、リクエストがControllerに届く前に共通処理を挟む仕組み
  • 認証チェックなどをMiddlewareにまとめることで、Controllerの重複コードを減らせる
  • auth のようによく使うMiddlewareはLaravelにあらかじめ用意されている
  • 独自のMiddlewareは php artisan make:middleware で生成し、$next($request) で後続処理に進める
  • 複数のMiddlewareは配列の左から順に実行されるため、チェックしたい順序で並べる

次に読むべき記事

RouteとController、Middlewareがひと通り分かったところで、次は画面の見た目を作るBladeテンプレートの基本文法を解説します。

→ 次の記事:Bladeテンプレートの基本文法

タイトルとURLをコピーしました