Laravel4、例外をグローバルに捕らえる

Tags : Laravel4  

404エラーを取り扱った前記事に引き続き、例外のお話です。現在ベータ4ですので、正式版になったら変更されるかも知れません。

なにはともあれ、処理をご覧ください。

App::error( function(Exception $exception, $code)
    {
        Log::error( $exception );
    } );

App::error( function (Symfony\Component\HttpKernel\Exception\HttpException $e, $code){
    switch ($code){
        case 200 :
            return Redirect::to('/');
        case 400 :
            return Response::view('errors.400', array(), '400');
        case 403 :
            return Response::view('errors.403', array(), '403');
        case 500 :
        default:
            return Response::view('errors.500', array(), '500');
    }
});

App::error( function ( Laradoc\Exceptions\ExecutionIssues\IdNotFound $e)
    {
        // 追記:app::abort()は使えません。素直にビューをリターンするほうが無難です。
        app::abort( '400', '指定された情報は見つかりません。' );
    } );

App::missing( function ( $e )
    {
        return Response::view('errors.404', array('message'=>$e->getMessage()), '404');
    } );

app/start/global.phpの中に定義しています。場所は自分の書いたコードが動作するより前であればどこでも良いのですが、ドキュメントでは、ここが勧められています。なお、ドキュメントには記載されていませんが、たぶん登録した順番で例外は処理されているようです。テストはしていないため、皆さんが活用するときには自分でも確認してください。

一番最初のExceptionを指定してある定義では、全ての例外を対象としています。ExceptionはPHPの例外の基底クラスです。全例外をログしています。

2番めの長い名前空間の例外、最後のクラス名がHttpExceptionの例外は、HTTPエラーを捕らえるものです。HTTPエラーとは以下の構文で発生する例外のことです。

App::abort('ステータスコード', 'メッセージ');

登録したロジックはswitch文でコードごとに、view/errorsフォルダー下に作成したエラーページを表示するようになっています。この例では固定ですが、例えばステータスコードに対応するファイルの存在をチェックし、存在すればそれをビューに使用する、存在しなければデフォルトとして500ページを表示するという仕組みにすることで、Laravel3の時と同様な仕組みを実現できます。

3つ目は、私が今作成しているシステムで使用している、マイ例外を捕らえています。例外の名前が示す通り、レコードのIDが見つからない場合に、404ページを表示するように前述のabortメソッドを使用しています。このようにハンドラーの中から、例外を発生させること出来ます。(追記:訂正です。できません。)

4つ目、最後は前記事で紹介した404エラーを捕らえるハンドラーを登録するための、missingメソッドです。多用されるため、特別なメソッドが用意されています。

この他にPHPのFatalエラーを捕らえるハンドラーを定義するfatalメソッドもあります。

どうです。例外を使ってプログラムしてみたくなったでしょ?

404エラー

404エラーはmissingメソッドでも、errorメソッドでSymfony\Component\HttpKernel\Exception\HttpExceptionを使用しても、どちらでも補足できます。

現在、ベータ4ですが、どうやら定義の後半に書いたほうが優先順序が高くなっているようです。そのため、2つ定義した場合、あとで定義したメソッドに制御が渡り、そこでリターンすれば、残りのメソッドには制御が渡りません。

missingは404専用ですから、どうしても404エラーを特別扱いしたい場合、今のところ最後に書くのが良いかも知れません。しかしmissingメソッドを使用せず、errorメソッドでHttpExceptionを捕まえ、ifで処理を書いたほうが意図がはっきりし、あとで見なおす場合わかりやすいかと思います。

注意

昨日丸一日悩み、githubにバグとして投げましたが、「例外ハンドラーの処理では、セッションは保存されない。」の一言で片付けられてしました。

つまり、セッションを経由してバリデーションエラーメッセージや、自分でビューにメッセージを送ったり、入力内容を送ったりすることは、ハンドラーの中からは簡単にできません。

よくある使用例を想定してみると:

App::missing(function($e) {
    Redirect::to('/')->with('message','このページは存在していません。');
});

App::error(function MyValidationException ($e) {
    Redirect::back()->withErrors($e->validator)->withInput();
});

このようなRidirectでセッションに保存を前提とするようなコードはハンドラに直接書けないという事です。多分、Sessionクラスでputかflushし、それを確実にセッションに書き込む必要があるようです。現在、追求していません。ベータ4ですから、変わる部分もあるでしょうし、この仕様は納得できるものではありませんしね。