46.

Laravel Controller 以外でリダイレクト|throwResponse / abort / Middleware

編集
この記事の要点
  • Laravel で Controller 以外(static メソッド / Service / Repository 等)からリダイレクトする方法
  • 通常の redirect() は Controller が return しないと効かない
  • 対処1: redirect()->throwResponse() で例外として送出 → どこからでもリダイレクト可
  • 対処2: abort_redirect(...)abort(...) でハンドラに任せる
  • 対処3: Middleware でリダイレクト返却(責務的に綺麗)

通常のリダイレクト

Laravel での通常のリダイレクトは Controller のメソッド戻り値として使います:

class HomeController extends Controller {
    public function show() {
        if (!auth()->check()) {
            return redirect('/login');  // ← Controller が return して初めて効く
        }
        return view('home');
    }
}

しかしリダイレクトしたい判定がもっと深いレイヤー(Service / Repository / static helper / Middleware 自体)にあるケースでは、戻り値を return で伝搬させるのが面倒です。

対処1: throwResponse() で例外として送出

Laravel の Response オブジェクトは throwResponse() メソッドで HttpResponseException を投げます。これはハンドラがキャッチして適切なレスポンスとして返却します:

namespace App\Services;

class PaymentService {
    public static function processPayment($order) {
        if ($order->isAlreadyPaid()) {
            // どこからでもリダイレクト可
            redirect('/orders/' . $order->id)
                ->with('error', 'すでに支払済みです')
                ->throwResponse();
        }
        // ... 通常処理
    }
}

// 呼び出し側はリダイレクトを意識する必要がない
class OrderController extends Controller {
    public function pay($id) {
        $order = Order::findOrFail($id);
        PaymentService::processPayment($order);  // ← ここでリダイレクトが投げられる
        return view('payment.complete');
    }
}

throwResponse()例外として処理を即座に止めてレスポンスを返すため、return の伝播が不要。Controller の戻り値処理を書く必要がありません。

対処2: abort_redirect() / abort()

Laravel には専用ヘルパーが用意されています:

// 直接リダイレクトを投げる
abort_redirect('/login');  // 302 リダイレクト
abort_redirect('/forbidden', 403);  // 任意のステータスコード

// HTTP エラーで停止(ハンドラがエラーページに飛ばす)
abort(404);
abort(403, '権限がありません');

// 条件付き
abort_if(!$user->canAccess($resource), 403);
abort_unless($user->isAdmin(), 403, '管理者のみ');

abort()HttpException を投げるので、これも例外ベース。エラーページに飛ばす場合に最適です。

対処3: Middleware でリダイレクト返却(推奨)

そもそもリダイレクトロジックはMiddleware に切り出すのが Laravel 流の綺麗な書き方:

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;

class EnsurePaymentNotYetMade {
    public function handle(Request $request, Closure $next) {
        $order = $request->route('order');
        if ($order && $order->isAlreadyPaid()) {
            return redirect()->route('orders.show', $order)
                ->with('error', 'すでに支払済みです');
        }
        return $next($request);
    }
}

// routes/web.php
Route::post('/orders/{order}/pay', [OrderController::class, 'pay'])
    ->middleware(EnsurePaymentNotYetMade::class);

responsable インターフェイス(Laravel 5.5+)

カスタムオブジェクトを直接 return して、変換ロジックで Response にする方法もあります:

namespace App\Responses;

use Illuminate\Contracts\Support\Responsable;

class RedirectToLogin implements Responsable {
    public function toResponse($request) {
        return redirect('/login')->with('error', 'ログインが必要です');
    }
}

// Controller / Service / どこでも
return new RedirectToLogin();  // フレームワークが Response に変換

各方式の使い分け

シーン推奨方式
Service / Repository の中で条件分岐が深いthrowResponse()(例外で即停止)
権限・認証エラーabort(403) / abort_if
共通の事前チェックMiddleware(責務分離が綺麗)
カスタムレスポンスを使い回すResponsable インターフェイス
Controller 内普通に return redirect()

注意点

  • throwResponse は例外を投げるのでスタックトレースに残る → ログレベル設定に注意
  • テストでの確認: $response->assertRedirect('/login') でリダイレクト先を検証
  • 多用は禁物: throwResponse / abort は便利だが、コードの流れが追いにくくなる → 可能なら Middleware を優先
  • HTMX / Ajax では HX-Redirect ヘッダ等の対応も必要なケースあり
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. インストールと設定
  2. クイックスタート & チュートリアル(初心者向け)
  3. クイックスタート & チュートリアル(中級者向け)
  4. ルーティング
  5. Bladeテンプレート(ビュー/レイアウト)
  6. コントローラー
  7. マイグレーションとテーブル定義
  8. データベースの設定
  9. Eloquentモデル (ORM)
  10. SQLとクエリビルダー
  11. バリデーション
  12. .envファイルの設定値へのアクセス
  13. 動作環境による分岐処理
  14. configフォルダ配下の設定値へのアクセス
  15. assetヘルパーを利用したpublicフォルダへのアクセス
  16. storageフォルダへのアクセス
  17. アプリケーション名の変更
  18. メンテナンス
  19. ログイン画面(認証システム)の作成
  20. ログインの必須化
  21. ログインユーザー情報の取得
  22. ルートの認証化
  23. 本番サーバーへのデプロイ方法
  24. 多言語化
  25. csrf_field
  26. ファイルのダウンロード
  27. CSVのアップロードおよび読み込み(maatwebsite/excel)
  28. ページタイトルの設定
  29. コマンド一覧
  30. エラー一覧
  31. SQLの実行ログ出力方法
  32. キャッシュのクリア
  33. Selectの結果の最初もしくは最後に任意の値を追加する方法
  34. ajaxでPOST通信する際の注意点
  35. ソーシャルログインの実装
  36. セッション情報の確認
  37. ログイン、ユーザー登録、パスワードリセット後のリダイレクト先の変更方法
  38. redirectやreturn viewにメッセージを付与する方法
  39. クッキー(cookie)の設定と取得
  40. クラスの再読み込み
  41. csrfの有効時間を変更する方法
  42. ViewComposerを用いてviewに共通の値を付与する方法
  43. View::shareを用いて共通の値を各ビューに渡す方法
  44. ミドルウェアを用いた処理の共通化
  45. Middleware内でAuth::check()などを使用する方法
  46. Controller以外でリダイレクトする方法
  47. セッションの値の取得/保存/更新/削除
  48. $requestの値を変更する方法
  49. 常時SSL化
  50. ページング(ページネーション)をする方法
  51. vue.jsとの連携
  52. Vue.jsと連携するSPA実行環境構築
  53. .envの値をvue.jsで参照する方法
  54. vue.jsを本番環境にリリースする方法
  55. could not find driver(Windows, MySQL編)