【Laravel】 エラーハンドラを使用してログや表示をカスタマイズ
エラー (例外) を投げたとき、どのように動かせばよいかを設定できます。500を返すのか、元のページに戻すのかなど見ていきましょう。
目次
例外のHandler
Laravelでは、例外排出時の挙動を設定できるHandlerがapp/Exceptions/Handler.php
に用意されています。
$dontReport
$dontReport
では、例外排出時にログに出力しない例外を指定します。あくまでログに出力しないというだけで、例外が起こっても処理を進めるものではありません。
protected $dontReport = [ \Spatie\LaravelIgnition\Exceptions\ViewException::class, // ... ];
$dontFlash
$dontFlash
は、バリデーション失敗時にセッションに一時保管しない値を指定するものです。例えば、パスワードの入力を間違えた際に元の画面に戻ると、メールアドレスの入力は引き継いでいますが、パスワードの入力は消えていますね。
これを$dontFlash
で指定します。
protected $dontFlash = [ 'current_password', 'password', 'password_confirmation', ];
register
register
メソッドでは、主にreportable
とrenderable
を使用した例外排出時のログ出力やページ表示に関する挙動の登録を行います。
reportable
では、特定の例外が投げられた場合にどのようなログを出力するかのカスタマイズなどが可能です。デフォルトのログを出力する場合はtrue
を返し、出力しない場合はfalse
を返すか->stop()
を付けます。
public function register() { // ViewException発生時、普通のログの代わりにログに"hogehoge"を出力する $this->reportable(function (\Spatie\LaravelIgnition\Exceptions\ViewException $e) { Log::debug("hogehoge"); return false; }); $this->reportable(function (Throwable $e) { // デフォルトのログ出力をしない return false; }); $this->reportable(function (Throwable $e) { // デフォルトのログ出力をしない })->stop(); $this->reportable(function (Throwable $e) { // デフォルトのログ出力をする return true; }); }
PHPで扱う例外は、全てThrowable
インターフェースを実装しています。
また、renderable
では、例外排出時にどのような内容を表示するのかを設定することができます。
$this->renderable(function (\Spatie\LaravelIgnition\Exceptions\ViewException $e, Request $request){ Log::debug($request); return response('View error.', 500); });
とはいえ、これだけではあまりに表示が簡素なので、ビューを返すことをおすすめします。
$this->renderable(function (\Spatie\LaravelIgnition\Exceptions\ViewException $e, Request $request){ Log::debug($request); return response()->view('dashboard', [/* viewに渡す値 */], 500); });
第2引数は、コントローラでview
関数を用いた時と同様に値を入れることができます。
第3引数はステータスコードです。
第4引数は省略していますが、レスポンスヘッダーを設定することが可能です。
return view(...)
だとステータスコードを入れるのが面倒です。そのため、response()->view(...)
がおすすめです。
上の例では画面こそダッシュボードの表示になりますが、ステータスコードはしっかり500
であることが分かります。

例外のクラス
アプリケーションの状態に応じて、独自の例外を出したいということはよくあることです。Laravelでは、例外に関するクラスを作成する場合にもartisan
を使用します。
php artisan make:exception HogeException
そうすると、app/Exception
ディレクトリ内に指定した名前の例外のクラスが追加されます。
namespace App\Exceptions; use Exception; class HogeException extends Exception { // }
この例外のクラスには実装がないため、自由に実装することができます。
use App\Models\User; use Exception; class HogeException extends Exception { private $user = null; public function __construct(User $user) { $this->user = $user; } }
$user = Auth::user(); throw new HogeException($user);
メッセージの設定
継承しているException
は、PHP組み込みの例外のクラスです。そのため、PHPと同じようにメッセージを設定できます。
class HogeException extends Exception { public function __construct() { // messageは元からあるメンバなので宣言不要 $this->message = "aaabbbccc"; } }
context
context
では、例外の出力内容に含めたい内容を返すことで、例外の内容を充実させることができます。
class HogeException extends Exception { private $user = null; public function __construct(User $user) { $this->user = $user; } public function context() { return [ 'user' => $this->user, 'foo' => 'bar', 'hoge' => 'fuga', ]; } }
[2022-03-27 09:14:16] local.ERROR: {"user":{"App\\Models\\User":{"id":1,"name":"mhgm","email":"xxxxxx@yyyyy.com","email_verified_at":"2022-03-26T13:27:10.000000Z","created_at":"2022-03-17T10:24:46.000000Z","updated_at":"2022-03-26T13:27:10.000000Z","tel":"hogehoge"}},"foo":"bar","hoge":"fuga","userId":1,"exception":"[object] (App\\Exceptions\\HogeException(code: 0): at /var/www/html/app/Http/Controllers/SettingsController.php:20) [stacktrace] #0 /var/www/html/vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\SettingsController->index() // ...
横に長いですが、たしかにUser
モデルの情報と、入れた文字列が出力に含まれていることが分かります。
例外ログの内容を変更
例外出力の内容を変更したい場合、report
メソッドを実装します。
Handler.php
で設定したreportable
と実質同じです。
class HogeException extends Exception { /** * ログの出力 * * @return boolean|void trueかvoidならデフォルトのログを出力しない、falseなら出力する (reportableと逆) */ public function report() { Log::error('hogehoge'); return true; } }
また、report
ではサービスコンテナが依存性の注入をしてくれます。
public function report(MyService $service) { Log::error($service->getHoge()); return false; }
例外時の画面出力を変更
例外を排出した際に、その例外特有の画面を出力したい場合、render
メソッドで出力内容やステータスコードを設定します。また、render
ではRequest
を受け取ることが可能なため、どのようなリクエストボディなどが設定されていたかを、コントローラと同様に取得することができます。
Handler.php
で設定したrenderable
と実質同じです。
class HogeException extends Exception { /** * 例外排出時の表示内容 * * @param Illuminate\Http\Request $request * @return mixed */ public function render(Request $request) { Log::debug($request->all()); // シンプルな文字列だけを表示する場合 return response('Hoge error.', 500); } }
renderable
と同様にview
を返すこともできます。
public function render(Request $request) { // dashboardの画面を表示 return response()->view('dashboard', [/* viewに渡すデータ */], 500); }
report
と違い、サービスコンテナは依存性の注入をしてくれません。
例外を排出せずにログだけ出力
例外が発生したけど動作は止めたくない、でもログは出力したいという場合、report
関数を用います。
try { throw new HogeException($user); } catch (Exception $e) { report($e); }
もし上のように自作のException
である場合、report
メソッドは実行されますがrender
メソッドは実行されません。
動作を中断してエラー出力
動作を中断して404を出したい等の場合、abort()
関数を用いると楽です。どこからでも使用できます。
abort(404); // メッセージ付きの場合 abort(500, 'hogehoge');
エラーページを作成する
404や500といったエラーページは、デフォルトではLaravelが用意したページが表示されます。しかし、本当にそのページをそのまま使うことはほぼ無いはずです。
カスタマイズするには、resources/views/errors
内に404.blade.php
のような形式で作成します。
Not Found <div> {{ $exception->getMessage() }} </div>
エラーページではAuth
が使えないので注意が必要です。
自作したException
でそれらのページを表示するようにしたい場合、response()->view
で指定して表示すると可能です。
class HogeException extends Exception { // ... public function render(Request $request) { return response()->view('errors.500', ['exception' => $this], 500); } }
{{ $exception->getMessage() }}
のように$exception
を使用する場合、'exception'
をビューに渡すデータに含める必要があります。
メッセージは、PHPの例外を継承した場合と同様に設定します。
class HogeException extends Exception { public function __construct() { $this->message = "aaabbbccc"; } }
abort()
関数を利用することでも可能ですが、その時点で処理を中断するという関数の意味を考えると適していません。
例外のあの画面を無効化する
開発環境で例外が発生した場合、例外のメッセージや出力位置、セッションの値などがページに表示されます。しかし、本番環境でそれを出力してしまうと大事故です。本番環境では必ず無効化しておく必要があります。
.env
のAPP_DEBUG
をfalse
に設定しておきましょう。
// ... APP_DEBUG=false // ...
まとめ
Laravelでは、例外を作成し、例外に応じた挙動を設定することができます。Laravel組み込みの例外の挙動を設定したい場合はHandler
でresponsable
などに設定を、自作の例外の場合はそれぞれの例外のクラスに設定すると良いです。
