この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:4
ページ更新者:guest
更新日時:2026-06-11 07:12:00

タイトル: Controller以外でリダイレクトする方法
SEOタイトル: 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 ヘッダ等の対応も必要なケースあり