【第四弾】Laravel入門資料
- Windows環境構築
- Mac環境構築
- Laravelとは
- ルーティング
- ビュー
- コントローラ
- マイグレーション
- モデル
- ここまでの知識の実践
- 検索・フィルタ
- ソフトデリート
- ページネーション
- 画像アップロード
- 認証ライブラリ
- 認証実装
- ミドルウェア・ロール
- 総合実践
📚 Laravel公式ドキュメント - Middleware / Authorization
https://laravel.com/docs/12.x/middleware https://laravel.com/docs/12.x/authorization
💡 この講座では、上記の公式ドキュメントを基に解説していきます。
公式ドキュメントは初学者には内容が難しいため、エッセンスを優しく噛み砕いて解説していきます。
ミドルウェアと権限管理
認証機能だけでは不十分です。誰が何をできるかを管理する権限管理(認可)が必要です。
この章では、ミドルウェアとロールを使った権限管理を学びます。
🎯 この章で学ぶこと
- ✅ 認証と認可の違い
- ✅ ミドルウェアの仕組みと使い方
- ✅ ロールテーブルの設計(シンプル版と多対多版)
- ✅ カスタムミドルウェアの作成
- ✅ Tinkerでのロール割り当てと権限チェック
認証と認可の違い
前章で学んだのは認証(Authentication)です。
この章で学ぶのは認可(Authorization)です。
認証(Authentication)
「あなたは誰ですか?」を確認すること。
ログイン機能がこれにあたります。
// 認証:ログインしているか確認
if (auth()->check()) {
echo 'ログインしています';
}
// 誰がログインしているか
echo auth()->user()->name; // 「田中太郎」
認可(Authorization)
「あなたには権限がありますか?」を確認すること。
管理者のみアクセス可能なページを作る機能がこれにあたります。
// 認可:管理者権限を持っているか確認
if (auth()->user()->isAdmin()) {
echo '管理者です';
}
// 編集者権限を持っているか
if (auth()->user()->hasRole('editor')) {
echo '記事を編集できます';
}
具体例で理解しよう
会社のオフィスに例えると:
-
認証: 社員証でゲートを通る
→ 「あなたは社員である」ことを確認 -
認可: 役職によって入れる部屋が異なる
→ 「一般社員は会議室Aにアクセスできるが、役員会議室には入れない」
Webアプリケーションの例
| ページ | 認証 | 認可 |
|---|---|---|
| トップページ | 不要 | 不要 |
| ダッシュボード | 必要(ログイン必須) | 不要 |
| 記事編集 | 必要(ログイン必須) | 必要(編集者または管理者) |
| ユーザー管理 | 必要(ログイン必須) | 必要(管理者のみ) |
✅ 認証と認可の違いが理解できました!
ミドルウェアの仕組み
ミドルウェアは、リクエストが処理される前やレスポンスが返される後に、
特定の処理を挟み込む仕組みです。
ミドルウェアの流れ:
1. ユーザーがリクエスト送信
↓
2. ミドルウェア1(例: CSRF保護)
↓
3. ミドルウェア2(例: 認証チェック)
↓
4. ミドルウェア3(例: 権限チェック)
↓
5. コントローラー処理
↓
6. レスポンス返却
Laravelの標準ミドルウェア
| ミドルウェア | 役割 |
|---|---|
auth |
ログインしているかチェック(認証) |
guest |
ログインしていないかチェック |
verified |
メール認証済みかチェック |
throttle |
レート制限(アクセス回数制限) |
authミドルウェアの使い方
// routes/web.php
// 方法1: 個別のルートに適用
Route::get('/dashboard', [DashboardController::class, 'index'])
->middleware('auth');
// 方法2: 複数のルートにまとめて適用
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', [DashboardController::class, 'index']);
Route::get('/profile', [ProfileController::class, 'show']);
Route::get('/settings', [SettingsController::class, 'index']);
});
// 方法3: コントローラーで適用
class DashboardController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
return view('dashboard');
}
}
authミドルウェアの動き:
- ログインしている → コントローラー処理を続行
- ログインしていない →
/loginにリダイレクト
✅ ミドルウェアの仕組みが理解できました!
ロールテーブルの設計(シンプル版)
まずは、usersテーブルにroleカラムを追加するシンプルな方法を学びます。
ステップ1: マイグレーション作成
# マイグレーション作成
php artisan make:migration add_role_to_users_table
// database/migrations/xxxx_add_role_to_users_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('role')->default('user');
});
}
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('role');
});
}
};
# マイグレーション実行
php artisan migrate
想定するロール:
'user'- 一般ユーザー(デフォルト)'editor'- 編集者(記事の作成・編集が可能)'admin'- 管理者(全ての操作が可能)
ステップ2: Userモデルにメソッド追加
// app/Models/User.php
class User extends Authenticatable
{
protected $fillable = [
'name',
'email',
'password',
'role', // ← 追加
];
/**
* 管理者かどうか
*/
public function isAdmin(): bool
{
return $this->role === 'admin';
}
/**
* 編集者かどうか
*/
public function isEditor(): bool
{
return $this->role === 'editor';
}
/**
* 特定のロールを持っているかチェック
*/
public function hasRole(string $role): bool
{
return $this->role === $role;
}
/**
* いずれかのロールを持っているかチェック
*/
public function hasAnyRole(array $roles): bool
{
return in_array($this->role, $roles);
}
}
ステップ3: Tinkerでロールを設定してみよう
# Tinkerを起動
php artisan tinker
// ユーザーを取得
$user = User::find(1);
// 現在のロールを確認
$user->role;
// => "user"(デフォルト)
// 管理者に昇格
$user->role = 'admin';
$user->save();
// 管理者かどうか確認
$user->isAdmin();
// => true
$user->hasRole('admin');
// => true
// 編集者ではない
$user->isEditor();
// => false
// 別のユーザーを編集者にする
$editor = User::find(2);
$editor->update(['role' => 'editor']);
// 編集者または管理者かチェック
$editor->hasAnyRole(['editor', 'admin']);
// => true(編集者なので)
// 一般ユーザーを作成
$normalUser = User::create([
'name' => '一般ユーザー',
'email' => 'user@example.com',
'password' => 'password',
'role' => 'user', // デフォルトは'user'なので省略可
]);
$normalUser->isAdmin();
// => false
$normalUser->hasRole('user');
// => true
✅ Tinkerで確認したこと:
- ロールの変更と保存
- isAdmin()、isEditor()での判定
- hasRole()での特定ロールチェック
- hasAnyRole()での複数ロールチェック
✅ シンプルなロール管理が実装できました!
カスタムミドルウェアの作成
管理者のみアクセス可能なページを作るため、カスタムミドルウェアを作成します。
ステップ1: ミドルウェア作成
# ミドルウェア作成
php artisan make:middleware IsAdmin
// app/Http/Middleware/IsAdmin.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class IsAdmin
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next): Response
{
// ログインしているか確認
if (!auth()->check()) {
return redirect('/login');
}
// 管理者かどうか確認
if (!auth()->user()->isAdmin()) {
abort(403, 'このページにアクセスする権限がありません。');
}
// 管理者なら次の処理へ
return $next($request);
}
}
ミドルウェアの動き:
- ログインしていない →
/loginにリダイレクト - ログインしているが管理者ではない → 403エラー(Forbidden)
- 管理者 → 次の処理(コントローラー)へ
ステップ2: ミドルウェアを登録
// bootstrap/app.php
use App\Http\Middleware\IsAdmin;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
// ミドルウェアのエイリアスを登録
$middleware->alias([
'admin' => IsAdmin::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
ステップ3: ルートに適用
// routes/web.php
// 管理者専用ページ
Route::middleware(['auth', 'admin'])->group(function () {
Route::get('/admin/dashboard', [AdminController::class, 'dashboard']);
Route::get('/admin/users', [AdminController::class, 'users']);
Route::post('/admin/users/{id}/role', [AdminController::class, 'updateRole']);
});
// または個別に適用
Route::get('/admin/dashboard', [AdminController::class, 'dashboard'])
->middleware(['auth', 'admin']);
ミドルウェアの順序:
['auth', 'admin']- まず認証チェック、次に管理者チェック- 順序が重要:認証されていないのに管理者チェックするとエラー
実践: Tinkerでテストしてみよう
# Tinkerを起動
php artisan tinker
// 管理者ユーザーを作成
$admin = User::create([
'name' => '管理者',
'email' => 'admin@example.com',
'password' => 'password',
'role' => 'admin',
]);
// ログインシミュレーション
Auth::login($admin);
// 現在のユーザーが管理者か確認
auth()->user()->isAdmin();
// => true
// 一般ユーザーを作成
$user = User::create([
'name' => '一般ユーザー',
'email' => 'user@example.com',
'password' => 'password',
'role' => 'user',
]);
// 一般ユーザーでログイン
Auth::login($user);
auth()->user()->isAdmin();
// => false
// この状態で /admin/dashboard にアクセスすると403エラー
編集者用ミドルウェアも作ってみよう
php artisan make:middleware IsEditorOrAdmin
// app/Http/Middleware/IsEditorOrAdmin.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class IsEditorOrAdmin
{
public function handle(Request $request, Closure $next): Response
{
if (!auth()->check()) {
return redirect('/login');
}
// 編集者または管理者かチェック
if (!auth()->user()->hasAnyRole(['editor', 'admin'])) {
abort(403, 'このページにアクセスする権限がありません。');
}
return $next($request);
}
}
// bootstrap/app.php
$middleware->alias([
'admin' => IsAdmin::class,
'editor' => IsEditorOrAdmin::class, // ← 追加
]);
// routes/web.php
// 編集者または管理者のみアクセス可能
Route::middleware(['auth', 'editor'])->group(function () {
Route::get('/posts/create', [PostController::class, 'create']);
Route::post('/posts', [PostController::class, 'store']);
Route::get('/posts/{id}/edit', [PostController::class, 'edit']);
Route::put('/posts/{id}', [PostController::class, 'update']);
});
✅ カスタムミドルウェアで権限管理ができました!
ロールテーブルの設計(多対多版)
シンプル版では1ユーザー1ロールでしたが、1ユーザーが複数ロールを持てる多対多の設計を学びます。
多対多が必要なケース:
- 「編集者」かつ「モデレーター」のような複数の役割
- プロジェクトごとに異なる権限を持つ
- より柔軟な権限管理が必要
テーブル設計
users テーブル
├─ id
├─ name
├─ email
└─ password
roles テーブル(ロール定義)
├─ id
├─ name (admin, editor, moderator など)
└─ description
role_user テーブル(中間テーブル)
├─ id
├─ user_id (外部キー → users.id)
└─ role_id (外部キー → roles.id)
マイグレーション作成
# rolesテーブル作成
php artisan make:migration create_roles_table
# 中間テーブル作成
php artisan make:migration create_role_user_table
// database/migrations/xxxx_create_roles_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('roles', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('description')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('roles');
}
};
// database/migrations/xxxx_create_role_user_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('role_user', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->onDelete('cascade');
$table->foreignId('role_id')->constrained()->onDelete('cascade');
$table->timestamps();
// 同じユーザーに同じロールを重複して割り当てないようにする
$table->unique(['user_id', 'role_id']);
});
}
public function down(): void
{
Schema::dropIfExists('role_user');
}
};
# マイグレーション実行
php artisan migrate
Roleモデル作成
php artisan make:model Role
// app/Models/Role.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Role extends Model
{
protected $fillable = ['name', 'description'];
/**
* このロールを持つユーザー
*/
public function users()
{
return $this->belongsToMany(User::class);
}
}
Userモデルにリレーション追加
// app/Models/User.php
class User extends Authenticatable
{
// ... 既存のコード ...
/**
* ユーザーが持つロール
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
/**
* 特定のロールを持っているかチェック
*/
public function hasRole(string $roleName): bool
{
return $this->roles()->where('name', $roleName)->exists();
}
/**
* いずれかのロールを持っているかチェック
*/
public function hasAnyRole(array $roleNames): bool
{
return $this->roles()->whereIn('name', $roleNames)->exists();
}
/**
* すべてのロールを持っているかチェック
*/
public function hasAllRoles(array $roleNames): bool
{
return $this->roles()->whereIn('name', $roleNames)->count() === count($roleNames);
}
/**
* ロールを割り当て
*/
public function assignRole(string $roleName): void
{
$role = Role::where('name', $roleName)->firstOrFail();
$this->roles()->syncWithoutDetaching($role);
}
/**
* ロールを削除
*/
public function removeRole(string $roleName): void
{
$role = Role::where('name', $roleName)->first();
if ($role) {
$this->roles()->detach($role);
}
}
}
Tinkerで多対多ロールを試そう
# Tinkerを起動
php artisan tinker
// ロールを作成
$adminRole = Role::create([
'name' => 'admin',
'description' => '管理者',
]);
$editorRole = Role::create([
'name' => 'editor',
'description' => '編集者',
]);
$moderatorRole = Role::create([
'name' => 'moderator',
'description' => 'モデレーター',
]);
// 作成されたロールを確認
Role::all();
// ユーザーを取得
$user = User::find(1);
// ロールを割り当て
$user->roles()->attach($adminRole);
$user->roles()->attach($editorRole);
// または
$user->assignRole('admin');
$user->assignRole('editor');
// ユーザーが持つロールを確認
$user->roles;
// => Collection {
// Role {id: 1, name: "admin", ...},
// Role {id: 2, name: "editor", ...}
// }
// ロール名だけ取得
$user->roles->pluck('name');
// => ["admin", "editor"]
// 特定のロールを持っているか確認
$user->hasRole('admin');
// => true
$user->hasRole('moderator');
// => false
// いずれかのロールを持っているか
$user->hasAnyRole(['editor', 'moderator']);
// => true(editorを持っている)
// すべてのロールを持っているか
$user->hasAllRoles(['admin', 'editor']);
// => true
$user->hasAllRoles(['admin', 'editor', 'moderator']);
// => false(moderatorを持っていない)
// ロールを削除
$user->removeRole('editor');
$user->roles->pluck('name');
// => ["admin"]
// 別のユーザーに複数ロールを一度に割り当て
$user2 = User::find(2);
$user2->roles()->attach([$editorRole->id, $moderatorRole->id]);
$user2->roles->pluck('name');
// => ["editor", "moderator"]
// 特定のロールを持つユーザーを検索
$editors = User::whereHas('roles', function ($query) {
$query->where('name', 'editor');
})->get();
// 管理者ロールを持つユーザー数をカウント
User::whereHas('roles', function ($query) {
$query->where('name', 'admin');
})->count();
// => 1
✅ Tinkerで確認したこと:
- Roleモデルでロール作成
- attach()でロール割り当て
- assignRole()とremoveRole()の使い方
- hasRole()、hasAnyRole()、hasAllRoles()での判定
- whereHas()で特定ロールを持つユーザー検索
✅ 多対多のロール管理が実装できました!
ミドルウェアの深いメカニズム(オニオンアーキテクチャ)
ミドルウェアがどのように動作するのか、オニオンアーキテクチャ(玉ねぎ構造)を理解しましょう。
オニオンアーキテクチャとは
Laravelのミドルウェアは、玉ねぎの層のようにリクエストを包んで処理します。
リクエストは外側の層から内側へ、レスポンスは内側から外側へと通過します。
公式ドキュメントの図:
Laravelの公式ドキュメントには、ミドルウェアのオニオン構造を示す図があります:
Laravel Middleware Documentation - Middleware and Responses
オニオンモデルのイメージ:
リクエスト →
↓
┌─────────────────────────┐
│ Middleware 1 (外側) │ ← CSRF保護
│ ┌───────────────────┐ │
│ │ Middleware 2 │ │ ← セッション管理
│ │ ┌─────────────┐ │ │
│ │ │ Middleware 3│ │ │ ← 認証チェック
│ │ │ ┌───────┐ │ │ │
│ │ │ │ Core │ │ │ │ ← コントローラー
│ │ │ │(中心)│ │ │ │
│ │ │ └───────┘ │ │ │
│ │ └─────────────┘ │ │
│ └───────────────────┘ │
└─────────────────────────┘
↓
レスポンス ←
リクエストとレスポンスの流れ
リクエストの流れ(外→内):
1. ユーザーがリクエスト送信
↓
2. Middleware 1 (CSRF保護)
- トークンをチェック
- OK → 次へ、NG → 403エラー
↓
3. Middleware 2 (セッション管理)
- セッションを開始
- セッションデータを読み込む
↓
4. Middleware 3 (認証チェック)
- ログインしているかチェック
- OK → 次へ、NG → /login にリダイレクト
↓
5. コントローラー実行
- ビジネスロジック処理
- レスポンスを生成
レスポンスの流れ(内→外):
5. コントローラーがレスポンスを返す
↓
4. Middleware 3 (認証チェック)
- レスポンスに何か追加できる(通常はそのまま通す)
↓
3. Middleware 2 (セッション管理)
- セッションを保存
- Set-Cookie ヘッダーを追加
↓
2. Middleware 1 (CSRF保護)
- レスポンスに何か追加できる(通常はそのまま通す)
↓
1. ユーザーにレスポンス返却
ミドルウェアの2つのタイプ
1. Before Middleware(リクエストの前処理)
リクエストがコントローラーに到達する前に処理を行います。
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class BeforeMiddleware
{
public function handle(Request $request, Closure $next)
{
// ここで前処理を実行
// 例: ログイン状態をチェック
if (!auth()->check()) {
return redirect('/login');
}
// 次のミドルウェア or コントローラーへ
return $next($request);
}
}
2. After Middleware(レスポンスの後処理)
コントローラーがレスポンスを生成した後に処理を行います。
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class AfterMiddleware
{
public function handle(Request $request, Closure $next)
{
// まず次のミドルウェア or コントローラーを実行
$response = $next($request);
// レスポンスが生成された後の処理
// 例: ヘッダーを追加
$response->header('X-Custom-Header', 'MyValue');
// ログを記録
\Log::info('Response generated', [
'url' => $request->url(),
'status' => $response->status(),
]);
return $response;
}
}
実例: ミドルウェアの適用順序を確認
// routes/web.php
Route::get('/test', function () {
\Log::info('コントローラー実行');
return 'Hello World';
})->middleware(['first', 'second', 'third']);
// app/Http/Middleware/FirstMiddleware.php
class FirstMiddleware
{
public function handle(Request $request, Closure $next)
{
\Log::info('FirstMiddleware: リクエスト前処理');
$response = $next($request);
\Log::info('FirstMiddleware: レスポンス後処理');
return $response;
}
}
// app/Http/Middleware/SecondMiddleware.php
class SecondMiddleware
{
public function handle(Request $request, Closure $next)
{
\Log::info('SecondMiddleware: リクエスト前処理');
$response = $next($request);
\Log::info('SecondMiddleware: レスポンス後処理');
return $response;
}
}
// app/Http/Middleware/ThirdMiddleware.php
class ThirdMiddleware
{
public function handle(Request $request, Closure $next)
{
\Log::info('ThirdMiddleware: リクエスト前処理');
$response = $next($request);
\Log::info('ThirdMiddleware: レスポンス後処理');
return $response;
}
}
ログ出力結果:
FirstMiddleware: リクエスト前処理 ← 外側から
SecondMiddleware: リクエスト前処理 ←
ThirdMiddleware: リクエスト前処理 ←
コントローラー実行 ← 中心
ThirdMiddleware: レスポンス後処理 ← 内側から
SecondMiddleware: レスポンス後処理 ←
FirstMiddleware: レスポンス後処理 ← 外側へ
重要なポイント:
- リクエストの流れ: 外側から内側へ(First → Second → Third → Controller)
- レスポンスの流れ: 内側から外側へ(Controller → Third → Second → First)
- $next($request) を呼ぶことで次の層へ渡す
- return $next($request) の前がBefore処理、後がAfter処理
ミドルウェアで処理を中断する
$next($request) を呼ばずに直接レスポンスを返すと、
それ以降のミドルウェアやコントローラーは実行されません。
class CheckAge
{
public function handle(Request $request, Closure $next)
{
if ($request->age < 18) {
// ここで処理を中断!
// コントローラーは実行されない
return redirect('/underage');
}
// 18歳以上なら次へ進む
return $next($request);
}
}
処理が中断された場合のフロー:
1. ユーザーがリクエスト送信(age=15)
↓
2. Middleware 1
↓
3. Middleware 2
↓
4. CheckAge ミドルウェア
- age < 18 を検出
- return redirect('/underage'); ← ここで中断!
↓
(Middleware 3 や コントローラーは実行されない)
↓
4. CheckAge ミドルウェア(レスポンス返却)
↓
3. Middleware 2(レスポンス返却)
↓
2. Middleware 1(レスポンス返却)
↓
1. ユーザーに /underage へのリダイレクトレスポンスが返る
グローバルミドルウェア vs ルートミドルウェア
| 種類 | 適用範囲 | 設定場所 |
|---|---|---|
| グローバル | 全てのリクエスト | bootstrap/app.php の withMiddleware() |
| ミドルウェアグループ | web or api グループ | bootstrap/app.php |
| ルートミドルウェア | 特定のルートのみ | routes/web.php で ->middleware() |
// bootstrap/app.php
return Application::configure(basePath: dirname(__DIR__))
->withMiddleware(function (Middleware $middleware) {
// グローバルミドルウェア(全てのリクエストに適用)
$middleware->append(MyGlobalMiddleware::class);
// webグループにミドルウェア追加
$middleware->web(append: [
MyWebMiddleware::class,
]);
// ミドルウェアエイリアス登録
$middleware->alias([
'admin' => IsAdmin::class,
'verified' => EnsureEmailIsVerified::class,
]);
})
->create();
Tinkerでミドルウェアの挙動を理解する
実際にミドルウェアを作って、動作を確認してみましょう。
# ミドルウェア作成
php artisan make:middleware LogMiddleware
// app/Http/Middleware/LogMiddleware.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class LogMiddleware
{
public function handle(Request $request, Closure $next)
{
// リクエスト前処理
\Log::info('リクエスト受信', [
'url' => $request->url(),
'method' => $request->method(),
'ip' => $request->ip(),
]);
// 次の処理へ
$response = $next($request);
// レスポンス後処理
\Log::info('レスポンス送信', [
'status' => $response->status(),
]);
return $response;
}
}
// bootstrap/app.php
$middleware->alias([
'log' => \App\Http\Middleware\LogMiddleware::class,
]);
// routes/web.php
Route::get('/test', function () {
return 'Hello World';
})->middleware('log');
ブラウザで /test にアクセスすると、storage/logs/laravel.log にログが記録されます。
✅ ミドルウェアのオニオンアーキテクチャが理解できました!
まとめ
この章で学んだこと
1. 認証と認可の違い
- 認証: 「あなたは誰?」を確認(ログイン機能)
- 認可: 「あなたには権限がある?」を確認(ロール・権限管理)
2. ミドルウェアの仕組み
- リクエストとレスポンスの間に処理を挟み込む
- オニオンアーキテクチャ(玉ねぎ構造)で多層処理
- Before Middleware(前処理)とAfter Middleware(後処理)
- $next($request)を呼ぶことで次の層へ
- 処理を中断してリダイレクトやエラーを返せる
3. ロール管理(シンプル版)
- usersテーブルにroleカラムを追加
- isAdmin()、hasRole()などのメソッド実装
- Tinkerでロール割り当てと確認
4. カスタムミドルウェア
- php artisan make:middleware で作成
- bootstrap/app.phpでエイリアス登録
- ルートに->middleware()で適用
- IsAdmin、IsEditorOrAdminなど用途別に作成
5. ロール管理(多対多版)
- roles、role_userテーブルで柔軟な権限管理
- 1ユーザーが複数ロールを持てる
- belongsToManyでリレーション定義
- attach()、detach()でロール割り当て・削除
- hasRole()、hasAnyRole()、hasAllRoles()で判定
- whereHas()で特定ロールを持つユーザー検索
6. オニオンアーキテクチャ
- ミドルウェアは玉ねぎの層構造
- リクエストは外→内、レスポンスは内→外
- middleware(['first', 'second'])の順序で適用
- グローバル、ミドルウェアグループ、ルートミドルウェア
シンプル版 vs 多対多版の使い分け
| シンプル版 | 多対多版 | |
|---|---|---|
| 構造 | usersテーブルにroleカラム | roles + role_user テーブル |
| ロール数 | 1ユーザー1ロール | 1ユーザー複数ロール |
| 実装難易度 | 簡単 | やや複雑 |
| 柔軟性 | 低い | 高い |
| 適している場合 | 小規模アプリ、シンプルな権限 | 大規模アプリ、複雑な権限管理 |
次のステップ
この章では、ミドルウェアとロールによる権限管理を学びました。
さらに学びたい方は以下のトピックに進んでください:
- Gate - クロージャベースの認可ロジック
- Policy - モデル単位の認可ロジック(誰がどの記事を編集できるか等)
- Permission(パーミッション) - より細かい権限管理(create, read, update, delete)
- Laravel Permission パッケージ - Spatie社の人気パッケージ
🎉 ミドルウェアとロール管理の章が完了しました!
認証と認可の違い、オニオンアーキテクチャ、ロールテーブルの設計、カスタムミドルウェアの作成、
そしてTinkerでの実践的な使い方を学びました。
これで実務で使える権限管理の基礎が身につきました!