見出し画像

【Laravel】Gateとルーティングに関して、学びのアウトプット


こんにちは!7月も早くも中盤になって、三連休初日ですね。
最近、LaravelのGate関連とルーティング周りのタスクを担当し、
初めてやったタスクなのでブログにしようと思いました。

内容としてシステムの申請処理をPostでした際にGateを使ってチェックするというものです。

今回はアプリケーションの一部をLaravelで実装するケースで作成してみようと思います。


📚 目次


1. 🚪 Gateって何?基本を押さえよう

Gateは、簡単に言えば「誰が何をできるか」を定義する機能です。例えば:

  • 管理者だけが記事を削除できる

  • 投稿者は自分の記事のみ編集できる

  • プレミアムユーザーだけが特定の機能にアクセスできる

このようなルールを簡単に設定できるんです!

Gateの設定方法

  1. `App\Providers\AuthServiceProvider.php` を開きます。

  2. `boot` メソッド内でGateを定義します。

use Illuminate\Support\Facades\Gate;

public function boot()
{
    $this->registerPolicies();

    Gate::define('manage-articles', function ($user) {
        return $user->role === 'admin';
    });

    Gate::define('edit-article', function ($user, $article) {
        return $user->id === $article->user_id || $user->role === 'admin';
    });
}

これで、「manage-articles」と「edit-article」という2つの権限が定義されました。

2. 🛣️ ルーティングの基本おさらい

ルーティングは、URLと処理(コントローラーのアクション)を紐付ける機能です。簡単に言えば、「どのURLにアクセスしたらどの処理を行うか」を決めるものです。

基本的なルーティング

`routes/web.php` でルートを定義します:

use App\Http\Controllers\ArticleController;

Route::get('/articles', [ArticleController::class, 'index']);
Route::get('/articles/{id}', [ArticleController::class, 'show']);
Route::post('/articles', [ArticleController::class, 'store']);
Route::put('/articles/{id}', [ArticleController::class, 'update']);
Route::delete('/articles/{id}', [ArticleController::class, 'destroy']);

これで、記事の一覧表示、詳細表示、作成、更新、削除の基本的なルートが設定されました。

3. 🔀 GateとルーティングのSタンパイ品の組み合わせ

さて、ここからが本題です。GateとルーティングSタンパイ品はどのように組み合わせて使うのでしょうか?

ミドルウェアを使用する方法

ルートにGateチェックを適用する最も簡単な方法は、`can` ミドルウェアを使うことです:

Route::put('/articles/{id}', [ArticleController::class, 'update'])
    ->middleware('can:edit-article,article');

Route::delete('/articles/{id}', [ArticleController::class, 'destroy'])
    ->middleware('can:manage-articles');

これで、記事の更新は記事の所有者か管理者のみが、削除は管理者のみが行えるようになりました。

4. 🚀 実践!ブログシステムを作ってみよう

では、実際のブログシステムを例に、GateとルーティングSタンパイ品の使い方を見てみましょう。

ステップ1:Gateの定義

public function boot()
{
    $this->registerPolicies();

    Gate::define('create-article', function ($user) {
        return $user->is_writer || $user->is_admin;
    });

    Gate::define('edit-article', function ($user, $article) {
        return $user->id === $article->user_id || $user->is_admin;
    });

    Gate::define('delete-article', function ($user) {
        return $user->is_admin;
    });

    Gate::define('submit-article', function ($user, $article) {
        return $user->id === $article->user_id && $article->status === 'draft';
    });

    Gate::define('cancel-article', function ($user, $article) {
        return $user->id === $article->user_id && $article->status === 'pending';
    });
}

ステップ2:ルーティングの設定

Route::post('/articles', [ArticleController::class, 'store'])
    ->middleware('can:create-article');

Route::put('/articles/{id}', [ArticleController::class, 'update'])
    ->middleware('can:edit-article,article');

Route::delete('/articles/{id}', [ArticleController::class, 'destroy'])
    ->middleware('can:delete-article');

Route::post('/articles/{id}/submit', [ArticleController::class, 'submit'])
    ->middleware('can:submit-article,article');

Route::post('/articles/{id}/cancel', [ArticleController::class, 'cancel'])
    ->middleware('can:cancel-article,article');

ステップ3:コントローラーの実装

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ArticleController extends Controller
{
    public function submit($id)
    {
        return $this->updateArticleStatus($id, 'pending', 'submit-article');
    }

    public function cancel($id)
    {
        return $this->updateArticleStatus($id, 'draft', 'cancel-article');
    }

    private function updateArticleStatus($id, $newStatus, $ability)
    {
        try {
            return DB::transaction(function () use ($id, $newStatus, $ability) {
                $article = Article::findOrFail($id);
                
                if (Gate::denies($ability, $article)) {
                    abort(403, '権限がありません。');
                }

                $article->status = $newStatus;
                $article->save();

                return redirect()->route('articles.show', $article->id)
                    ->with('success', '記事のステータスが更新されました。');
            });
        } catch (\Exception $e) {
            Log::error('記事ステータス更新エラー: ' . $e->getMessage());
            return back()->withErrors('記事の更新中にエラーが発生しました。');
        }
    }
}

5. 🛡️ エラーハンドリングで完璧を目指す

最後に、エラーハンドリングを実装して、アプリケーションをさらに堅牢にしましょう。

app/Exceptions/Handler.php の修正

use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class Handler extends ExceptionHandler
{
    public function render($request, Throwable $exception)
    {
        if ($exception instanceof AuthorizationException) {
            return response()->view('errors.403', [], 403);
        }

        if ($exception instanceof ModelNotFoundException) {
            return response()->view('errors.404', [], 404);
        }

        return parent::render($request, $exception);
    }
}

これで、権限エラーや「Not Found」エラーが発生した場合、ユーザーフレンドリーなエラーページが表示されるようになりました。

🎉 まとめ

今回は、LaravelのGateとルーティングを使って、セキュアなWebアプリケーションを構築する方法を紹介しました。

  1. Gateで詳細な権限設定ができる

  2. ルーティングとGateを組み合わせて、URLレベルでのアクセス制御が可能

  3. トランザクション処理でデータの整合性を保証

  4. エラーハンドリングでユーザーエクスペリエンスを向上

これらの技術を使いこなすことで、より安全で使いやすいWebアプリケーションが作れると思ってます。

今週もお疲れ様でした。! 🚀✨

この記事が参加している募集

この記事が気に入ったらサポートをしてみませんか?