Inertia.jsで複数のバリデーションルールを指定した際に最初の1つしかエラーメッセージが表示されない理由
はじめに
Laravelでは通常、以下のようなバリデーションエラーのメッセージが返ってきます。
'errors' => [
'tel' => [
0 => '電話番号には整数を指定してください。',
1 => '電話番号には11以下の数字を指定してください。',
],
]
一方でInertia.jsを使うと各項目で最初に発生したバリデーションエラーのメッセージのみが返ってきます。Inertia.jsのValidationに関するドキュメントにもある通り、フロントエンドで扱う場合もerrors.telは配列ではなく文字列となります。
'errors' => [
'tel' => '電話番号には整数を指定してください。',
]
この違いがどこからどこから生まれるのか気になったので、今回はInertiaのコードを読み解いてみました。
結論
まずは結論から。各項目のメッセージの配列→文字列への変換はMiddleware.phpのresolveValidationErrors()で行われていました。
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()で呼ばれています。
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