Inertia.jsで複数のバリデーションルールを指定した際に最初の1つしかエラーメッセージが表示されない理由

2024/08/16に公開

はじめに

Laravelでは通常、以下のようなバリデーションエラーのメッセージが返ってきます。

'errors' => [
    'tel' => [
        0 => '電話番号には整数を指定してください。',
        1 => '電話番号には11以下の数字を指定してください。',
    ],
]

一方でInertia.jsを使うと各項目で最初に発生したバリデーションエラーのメッセージのみが返ってきます。Inertia.jsのValidationに関するドキュメントにもある通り、フロントエンドで扱う場合もerrors.telは配列ではなく文字列となります。

'errors' => [
    'tel' => '電話番号には整数を指定してください。',
]

この違いがどこからどこから生まれるのか気になったので、今回はInertiaのコードを読み解いてみました。

結論

まずは結論から。各項目のメッセージの配列→文字列への変換はMiddleware.phpのresolveValidationErrors()で行われていました。

/vendor/inertiajs/inertia-laravel/src/Middleware.php
fction resolveValidationErrors(Request $request)
{
    if (! $request->hasSession() || ! $request->session()->has('errors')) {
        return (object) [];
    }

    return (object) collect($request->session()->get('errors')->getBags())->map(function ($bag) {
        return (object) collect($bag->messages())->map(function ($errors) {
            return $errors[0];
        })->toArray();
    })->pipe(function ($bags) use ($request) {
        if ($bags->has('default') && $request->header(Header::ERROR_BAG)) {
            return [$request->header(Header::ERROR_BAG) => $bags->get('default')];
        }

        if ($bags->has('default')) {
            return $bags->get('default');
        }

        return $bags->toArray();
    });
}

$errorsの中身は各項目のエラーメッセージとなっております。中身は通常のバリデーションと同様にエラーメッセージを要素に持つ配列になっています(以下の例は項目telの場合)。

[
    0 => '電話番号には整数を指定してください。',
    1 => '電話番号には11以下の数字を指定してください。',
],

この配列の最初の要素をreturn $errors[0];で取得しているため、冒頭で述べた通り各項目で最初に発生したバリデーションエラーのメッセージのみが返ってきているようです。

呼び出し箇所

エラーメッセージの配列→文字列への変換を行うresolveValidationErrors()は同じMiddleware.php内のshare()で呼ばれています。

/vendor/inertiajs/inertia-laravel/src/Middleware.php
public function share(Request $request)
{
    return [
        'errors' => Inertia::always($this->resolveValidationErrors($request)),
    ];
}

上記のMiddleware.phpに書かれたshare()はHandleInertiaRequests.phpというファイルから呼ばれています。こちらのHandleInertiaRequests.phpのshare()の戻り値に書かれたデータは全てのリクエストに共通して返却されるようになります。この部分でparent::share($request)を呼び出すことで、配列→文字列へ変換されたエラーメッセージがフロントエンドに渡されています。

public function share(Request $request): array
{
    return array_merge(parent::share($request), [
        //
    ]);
}

Discussion