Laravel 10.x エラー処理

イントロダクション

エラーと例外の処理は、新しいLaravelプロジェクトの開始時に最初から設定されています。App\Exceptions\Handlerクラスは、アプリケーションが投げるすべての例外がログに記録され、ユーザーへレンダする場所です。このドキュメント全体を通して、このクラスについて詳しく説明します。

設定

config/app.php設定ファイルのdebugオプションは、エラーに関する情報が実際にユーザーに表示される量を決定します。デフォルトでは、このオプションは、.envファイルに保存されているAPP_DEBUG環境変数の値を尊重するように設定されています。

ローカル開発中は、APP_DEBUG環境変数をtrueに設定する必要があります。実稼働環境では、この値は常にfalseである必要があります。本番環境で値がtrueに設定されていると、機密性の高い設定値がアプリケーションのエンドユーザーに公開されるリスクが起きます。

例外ハンドラ

例外のレポート

すべての例外は、App\Exceptions\Handlerクラスが処理します。このクラスは、カスタム例外レポートとレンダリングコールバックを登録できるregisterメソッドを持っています。こうした各概念について詳しく説明します。例外レポートは、例外をログに記録したり、FlareBugsnagSentryなどの外部サービスへ送信したりするために使用します。デフォルトで例外はログ設定に基づいてログに記録します。ただし、必要に応じて例外を自由に記録できます。

さまざまなタイプの例外をさまざまな方法で報告する必要がある場合は、reportableメソッドを使用して、特定のタイプの例外を報告する必要があるときに実行するクロージャを登録できます。Laravelは、クロージャのタイプヒントを調べることで、クロージャが報告する例外のタイプを決定します。

use App\Exceptions\InvalidOrderException;

/**
 * アプリケーションの例外処理コールバックを登録
 */
public function register(): void
{
    $this->reportable(function (InvalidOrderException $e) {
        // ...
    });
}

reportableメソッドを使用してカスタム例外レポートコールバックを登録した場合でも、Laravelはアプリケーションのデフォルトのログ設定を使用して例外をログに記録します。デフォルトのログスタックへ例外の伝播を停止する場合は、レポートコールバックを定義するときにstopメソッドを使用するか、コールバックからfalseを返します。

$this->reportable(function (InvalidOrderException $e) {
    // ...
})->stop();

$this->reportable(function (InvalidOrderException $e) {
    return false;
});

Note: 特定の例外のレポートをカスタマイズするには、レポート可能な例外を利用することもできます。

グローバルログコンテキスト

利用可能な場合、Laravelは現在のユーザーのIDをコンテキストデータとしてすべての例外のログメッセージに自動的に追加します。アプリケーションのApp\Exceptions\Handlerクラスのcontextメソッドを定義することで、独自のグローバルコンテキストデータを定義できます。この情報は、アプリケーションによって書き込まれるすべての例外のログメッセージに含まれます。

/**
 * ログ用のデフォルトのコンテキスト変数を取得
 *
 * @return array<string, mixed>
 */
protected function context(): array
{
    return array_merge(parent::context(), [
        'foo' => 'bar',
    ]);
}

例外ログコンテキスト

すべてのログメッセージにコンテキストを追加することは便利ですが、特定の例外にはログに含めたい固有のコンテキストがある場合もあります。アプリケーションの例外へcontextメソッドを定義することにより、例外のログエントリに追加すべき関連データを指定できます。

<?php

namespace App\Exceptions;

use Exception;

class InvalidOrderException extends Exception
{
    // ...

    /**
     * 例外のコンテキスト情報を取得
     *
     * @return array<string, mixed>
     */
    public function context(): array
    {
        return ['order_id' => $this->orderId];
    }
}

reportヘルパ

場合により、例外を報告する必要はあるが、現在のリクエストの処理を続行する必要がある場合もあります。reportヘルパ関数を使用すると、エラーページをユーザーに表示せずに、例外ハンドラを介して例外をすばやく報告できます。

public function isValid(string $value): bool
{
    try {
        // 値のバリデーション…
    } catch (Throwable $e) {
        report($e);

        return false;
    }
}

Deduplicating Reported Exceptions

アプリケーション全体でreport関数を使用している場合、同じ例外を複数回報告することがあり、ログに重複したエントリが作成されることがあります。

一つの例外インスタンスが一度だけ報告されるようにしたい場合は、アプリケーションのAppExceptionsクラスの中で$withoutDuplicatesプロパティをtrueに設定してください。

namespace App\Exceptions;

use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    /**
     * 例外インスタンスが一度だけ報告されるべきであることを示す
     *
     * @var bool
     */
    protected $withoutDuplicates = true;

    // ...
}

これで、同じ例外インスタンスでreportヘルパが呼び出された場合、最初に呼び出されたものだけが報告されるようになります。

$original = new RuntimeException('Whoops!');

report($original); // レポートされる

try {
    throw $original;
} catch (Throwable $caught) {
    report($caught); // 無視される
}

report($original); // 無視される
report($caught); // 無視される

例外のログレベル

アプリケーションのログにメッセージが書き込まれるとき、そのメッセージは指定されたログレベルで書かれ、これは書き込まれるメッセージの緊急度や重要度を表します。

前記のように、reportableメソッドを使用してカスタム例外レポートコールバックを登録した場合でも、Laravelはアプリケーションのデフォルトログ設定を使用して例外を記録します。しかし、ログレベルはメッセージを記録するチャンネルへ影響を与えることがあるため、特定の例外を記録するログレベルを設定したいでしょう。

これを実現するにはアプリケーションの例外ハンドラへ、$levelsプロパティを定義します。このプロパティには、例外の種類とそれに関連するログレベルの配列を格納する必要があります:

use PDOException;
use Psr\Log\LogLevel;

/**
 * 例外タイプと関係するカスタムログレベルのリスト
 *
 * @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
 */
protected $levels = [
    PDOException::class => LogLevel::CRITICAL,
];

タイプによる例外の無視

アプリケーションを構築するときに、絶対に報告したくないタイプの例外が発生することがあります。こうした例外を無視するには、アプリケーションの例外ハンドラへ、$dontReportプロパティを定義します。このプロパティへ追加したクラスは、決して報告されませんが、カスタムレンダロジックを持つ可能性があります。

use App\Exceptions\InvalidOrderException;

/**
 * レポートしない例外タイプのリスト
 *
 * @var array<int, class-string<\Throwable>>
 */
protected $dontReport = [
    InvalidOrderException::class,
];

内部的に、Laravelは予めいくつかのタイプのエラーを無視します。例えば、404 HTTPエラーや無効なCSRFトークンで生成された419 HTTPレスポンスから生じる例外などです。Laravelに指定する種類の例外を無視しないように指示したい場合は、例外ハンドラのregisterメソッド内でstopIgnoringメソッドを呼び出します。

use Symfony\Component\HttpKernel\Exception\HttpException;

/**
 * アプリケーションの例外処理コールバックを登録
 */
public function register(): void
{
    $this->stopIgnoring(HttpException::class);

    // ...
}

例外のレンダ

Laravelはデフォルトで、例外ハンドラが例外をHTTPレスポンスに変換します。しかし、指定タイプの例外に対して、カスタムレンダクロージャを自由に登録することもできます。例外ハンドラ内で、renderableメソッドを呼び出してください。

renderableメソッドへ渡すクロージャは、Responseヘルパを介して生成されるIlluminate\Http\Responseのインスタンスを返す必要があります。Laravelは、クロージャのタイプヒントを調べることで、どのタイプの例外をクロージャがレンダするのか決定します。

use App\Exceptions\InvalidOrderException;
use Illuminate\Http\Request;

/**
 * アプリケーションの例外処理コールバックを登録
 */
public function register(): void
{
    $this->renderable(function (InvalidOrderException $e, Request $request) {
        return response()->view('errors.invalid-order', [], 500);
    });
}

また、renderableメソッドを使い、NotFoundHttpExceptionなどのLaravelやSymfonyの組み込み例外のレンダ動作をオーバーライドすることもできます。renderableメソッドに指定したクロージャが値を返さない場合は、Laravelのデフォルト例外レンダが利用されます。

use Illuminate\Http\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * アプリケーションの例外処理コールバックを登録
 */
public function register(): void
{
    $this->renderable(function (NotFoundHttpException $e, Request $request) {
        if ($request->is('api/*')) {
            return response()->json([
                'message' => 'Record not found.'
            ], 404);
        }
    });
}

Reportable/Renderable例外

例外ハンドラのregisterメソッドで、カスタムレポートやレンダ動作を定義する代わりに、アプリケーションの例外へ直接reportrenderメソッドを定義することもできます。これらのメソッドが存在する場合、フレームワークが自動的に呼び出します。

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class InvalidOrderException extends Exception
{
    /**
     * 例外を報告
     */
    public function report(): void
    {
        // ...
    }

    /**
     * 例外をHTTPレスポンスへレンダリング
     */
    public function render(Request $request): Response
    {
        return response(/* ... */);
    }
}

LaravelやSymfonyの組み込み済み例外など、既存のレンダリング可能な例外を拡張している場合は、例外のrenderメソッドからfalseを返し、例外のデフォルトHTTPレスポンスをレンダできます。

/**
 * Render the exception into an HTTP response.
 */
public function render(Request $request): Response|bool
{
    if (/** この例外をレポートする必要があるかを判断… */) {

        return response(/* ... */);
    }

    return false;
}

特定の条件が満たされた場合にのみ必要なカスタムレポートロジックが例外に含まれている場合は、デフォルトの例外処理設定を使用して例外をレポートするようにLaravelに指示する必要が起き得ます。これを行うには、例外のreportメソッドからfalseを返します。

/**
 * 例外を報告
 */
public function report(): bool
{
    if (/** この例外をレポートする必要があるかを判断… */) {

        // ...

        return true;
    }

    return false;
}

Note: reportメソッドで必要な依存関係をタイプヒントすると、Laravelのサービスコンテナがメソッドへ自動的に依存を注入します。

HTTP例外

一部の例外は、サーバからのHTTPエラーコードを表します。たとえば、「ページが見つかりません」エラー(404)、「不正なエラー」(401)、または開発者が500エラーを生成する可能性もあります。アプリケーションのどこからでもこのようなレスポンスを生成したい場合は、abortヘルパを使用できます。

abort(404);

カスタムHTTPエラーページ

Laravelを使用すると、さまざまなHTTPステータスコードのカスタムエラーページを簡単に表示できます。たとえば、404 HTTPステータスコードのエラーページをカスタマイズする場合は、resources/views/errors/404.blade.phpビューテンプレートを作成します。このビューは、アプリケーションが生成するすべての404エラーでレンダされます。このディレクトリ内のビューには、対応するHTTPステータスコードと一致する名前を付ける必要があります。abort関数によって生成されたSymfony\Component\HttpKernel\Exception\HttpExceptionインスタンスは$exception変数としてビューに渡されます。

<h2>{{ $exception->getMessage() }}</h2>

vendor:publish Artisanコマンドを使用して、Laravelのデフォルトのエラーページテンプレートをリソース公開できます。テンプレートをリソース公開したら、好みに合わせてカスタマイズしてください。

php artisan vendor:publish --tag=laravel-errors

HTTPエラーページのフォールバック

一連のHTTPステータスコードに対応する「フォールバック」エラーページを定義することもできます。このページは、発生した特定のHTTPステータスコードに対応するページが存在しない場合にレンダされます。これには、アプリケーションのresources/views/errorsディレクトリに、4xx.blade.phpテンプレートと5xx.blade.phpテンプレートを定義します。

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる