Laravelには認可機能として
- Gate
- Policy
が用意されています。
ただ正直なところ、
- とりあえず
if ($user->role === 'admin') - Bladeで
@if書いて制御
みたいな実装をしてしまいがちです。
今回は、実務で認可設計を整理したときの考え方をまとめます。
よくあるアンチパターン
① Controllerに直接role判定を書く
if ($user->role !== 'admin') {
abort(403);
}
② Bladeでroleを直接見る
@if(auth()->user()->role === 'admin')
<button>削除</button>
@endif
一見シンプルですが、
- ロジックが散らばる
- 変更に弱い
- テストしづらい
- 権限変更時に全探索が必要
という問題があります。
Laravelの正しい選択肢
Laravelでは認可を
- Gate(行為ベース)
- Policy(モデルベース)
に分けて設計できます。
GateとPolicyの違い
Gate
特定の行為に対する認可
例:
- 管理画面に入れるか
- レポートをエクスポートできるか
- 特定の操作を実行できるか
→ モデルに紐づかない
Policy
特定のモデルに対する操作の認可
例:
- このPostを編集できるか
- このUserを削除できるか
- このOrderを更新できるか
→ モデルが存在する
使い分けの基準
| 状況 | 使うべきもの |
|---|---|
| モデルに対するCRUD | Policy |
| 単なる行為の許可 | Gate |
| 画面表示制御 | @can |
| 実行時のチェック | authorize() |
実装例
① Gateの定義
App\Providers\AuthServiceProvider.php
use Illuminate\Support\Facades\Gate;
public function boot()
{
Gate::define('access-admin-panel', function ($user) {
return $user->role === 'admin';
});
}
Controllerで使用
public function index()
{
$this->authorize('access-admin-panel');
return view('admin.index');
}
Bladeで使用
@can('access-admin-panel')
<a href="/admin">管理画面</a>
@endcan
Policyの実装例
① Policy作成
php artisan make:policy PostPolicy --model=Post
② Policy定義
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
③ Controllerで使用
public function update(Post $post)
{
$this->authorize('update', $post);
// 更新処理
}
④ Bladeで使用
@can('update', $post)
<button>編集</button>
@endcan
なぜ@ifではなく@canを使うのか?
@if(auth()->user()->role === 'admin') と書くと
- ロール変更時に全修正が必要
- 将来的な権限拡張に弱い
- 意図が分かりづらい
一方 @can なら、
「何ができるか」という抽象的な表現になる
設計が行為ベースになるのがポイントです。
設計を整理して気づいたこと
認可設計で重要なのは
ユーザーが何者かではなく、何ができるか
roleはあくまで内部実装。
ControllerやBladeには
「行為」だけを書きます。
まとめ
Laravelで認可を整理するなら:
- モデルに対する操作 → Policy
- モデルに紐づかない行為 → Gate
- 表示制御は
@can - 実行時は
authorize()を必ず通す - roleを直接見ない
これだけで設計がかなりスッキリします。
最初は少し手間に感じますが、
ロジックが一箇所に集約され、
後からの変更にも強い構成になります。
Laravelの認可は強力なので、
「なんとなくif」から卒業するだけで
コードの質はかなり上がります。