Laravel

Laravelのバリデーション|validateとFormRequestの使い分け・カスタムルール・JSON応答

Laravelのバリデーション|validateとFormRequestの使い分け・カスタムルール・JSON応答

Laravelで入力検証を書く経路は3つあります。コントローラの中で$request->validate()を呼ぶ形、FormRequestクラスへ切り出す形、Validatorファサードでインスタンスを組み立てる形。どれもルールの記法は共通なので、選ぶ基準は「ルールをどこに置くか」ではなく「失敗したときに誰が応答を決めるか」に置くと迷いません。加えて13系では、プロパティで書いていた設定がPHP属性で書けるようになり、未知のフィールドを拒否する仕組みも入りました。検証の目的や種類そのものは入力検証の考え方を整理した解説で扱うため、ここではLaravelの実装に絞ります。

まとめ:3経路の選び分けと13系で変わった書き方の要点

先に結論を並べます。版に依存する記述は、2026年8月29日時点の13.xのソースと公式ドキュメントで確認しました。

  • 既定は$request->validate()。失敗時は自動でリダイレクトかJSONを返すため、成功系のコードだけ書けば済みます。
  • FormRequestへ切り出す基準は再利用の有無ではなく、認可判定・相関チェック・応答の作り替えが要るかどうか。
  • Validatorファサードは、HTTPリクエスト以外の入力(バッチ、コンソールコマンド)を検証するときに使います。
  • 13系ではStopOnFirstFailure・RedirectTo・RedirectToRoute・ErrorBagの4つが属性で書けます。12系のプロパティ記法も動きます。
  • 13系で加わったFailOnUnknownFields属性は、ルールに無いキーが送られたら弾く挙動。既定はオフです。
  • カスタムルールはValidationRuleインタフェースを実装する形。使い捨てならクロージャ直書きで足ります。
  • API開発では422とerrorsの構造をフロントとの契約として固定します。ネストしたキーはドット記法へ潰されます。

Laravelでバリデーションを書く3経路と既定に据える基準

3つの経路は優劣ではなく、責務の置き場所が違うだけです。記法は共通で書き換えのコストは低く、必要が出た時点で移すほうが結果的に速く進みます。

コントローラ内のvalidateが返す値と黙って素通りする入力

アクションの中で$request->validate()を呼ぶと、検証を通った値だけが配列で返ります。ここが要点。返り値を捨てて$request->all()を続けて渡すと、ルールを書いた意味が半減します。検証済みの配列をそのまま代入や更新へ流すのが基本形で、意図しないカラムの書き換えを防ぐ効果もあります。

もう一点、素のvalidate()はルールに書かれていないキーを黙って無視します。余計なキーが混ざってもエラーにはならず、返り値から落ちるだけ。ルール外のキーを拒否したい場合は、後述するFormRequest側の仕組みが要ります。失敗時の動きは経路で分かれ、通常のHTTPリクエストなら直前のURLへリダイレクトし、JSONを期待するリクエストならステータス422でエラー本体を返します。この分岐はフレームワーク側が持つため、コントローラに条件分岐は不要です。

FormRequestへ切り出す基準は再利用の有無ではなく責務

「同じルールを2箇所で使うから切り出す」という理由づけをよく見かけますが、判断軸としては弱いほうでしょう。切り出しが効くのは、検証の前後に別の判断が挟まるときです。認可、複数項目の関係を見る相関チェック、失敗時の応答形式やリダイレクト先の指定。この3つをコントローラに置くと、アクションが検証と業務処理の両方を抱えて読みにくくなります。

生成はphp artisan make:request StorePostRequestで、アプリケーション配下の Http Requests ディレクトリにクラスができます。クラス名や配置の規約はLaravelの命名規則をまとめた記事に揃えると迷いが減るはずです。あとはコントローラの引数の型をこのクラスに変えるだけで、アクションに入る前に検証が終わります。

注意したいのがauthorize()。雛形では真偽値を返すメソッドが用意されており、falseを返すと403で止まります。ここを空のままtrue固定にして公開すると、認可の穴がクラスの中に残ります。ポリシーを呼ぶか、認可はミドルウェア側で担保済みだとコメントを添えるかを決めておいてください。

Validatorファサードを使うのはHTTP以外の入力のとき

3つ目はValidatorファサードで検証インスタンスを自分で作る形。検証対象の配列とルールを明示的に渡すため、リクエストに紐づきません。CSV取り込みのバッチ、コンソールコマンドの引数、外部APIから受け取ったレスポンスの検証といった場面で使えます。

もう一つの使いどころは、失敗しても例外を投げたくない場合。fails()で判定して自分で分岐できるため、通った行だけ取り込み、落ちた行はエラー一覧に積む処理が書けます。取り込み系のバッチはこの形が扱いやすいでしょう。

経路 失敗時の応答 向いている場面
validateメソッド 自動でリダイレクト/422 画面フォームの標準形
FormRequest 自動+認可と応答の制御 認可や相関がある処理
Validatorファサード 自分で分岐する バッチ・コンソール

FormRequestの実装とLaravel 13系で属性化された指定方法

FormRequestの中身はルール定義だけではありません。メッセージ、項目名、検証後の追加判定、失敗時の挙動まで、このクラス1つに集約できます。そして13系では、この失敗時の挙動の書き方が変わりました。

rulesとmessagesとattributesの3メソッドで完結させる

rules()が返す配列に書けるのは、フレームワークが用意した規則、カスタムルールのインスタンス、クロージャの3種類。DBの一意性を見るunique規則のように、テーブルの状態を参照する規則も混ぜられます。カラムの取りうる値をmigration側で絞っているなら、enumカラムの定義と値の追加を扱った記事と合わせ、in規則の候補値をDB定義と一致させておくと不整合が起きにくくなります。

messages()はルール単位のメッセージ、attributes()は項目名の表示を差し替えるメソッド。「emailは必須です」ではなく「メールアドレスは必須です」と出したいなら、messagesに全文を書くよりattributesで項目名だけ置き換えるほうが保守は軽くなります。ルールが増えても項目名の定義は1箇所で済むからです。

検証を通ったあとに追加で判定したいときはafter()を使います。クロージャの配列を返す形で、その中からバリデータのエラー集合へ直接メッセージを足せる仕組み。在庫数との突き合わせなど規則で表せない業務判断はここに置くと、応答の形を揃えたまま拡張できます。

12系のプロパティ指定が13系で属性指定に置き換わった実測の差分

ここが版をまたぐ実装で引っかかりやすい点です。12系のドキュメントでは、最初の失敗で検証を打ち切る設定や、リダイレクト先の指定をクラスのプロパティで書くと案内されていました。13系のドキュメントでは同じ設定がクラスに付けるPHP属性として示されています。StopOnFirstFailure、RedirectTo、RedirectToRoute、ErrorBagの4つ。

実際に13.xのソースを読むと、属性を読み取って既存のプロパティへ代入する処理が入っており、プロパティ自体も残っています。つまり12系の書き方をそのまま持ち込んでも動き、移行時に全部書き換える必要はありません。ただし同じクラスで両方の記法が混在すると、設定を探す手間が生まれます。プロジェクト単位でどちらかに寄せる方針を決めておくのが落としどころでしょう。

自分の環境がどちらの版かは、記法の判断より先に確かめておいてください。手順はLaravelのバージョン確認とサポート期限を整理した記事にまとめています。12系はバグ修正の提供が2026年8月13日で終わり、残るのはセキュリティ修正だけという段階に入りました。

failedValidationを上書きして失敗時の応答を作り替える

属性で表せないほど応答を作り込みたい場合は、failedValidation()を上書きします。既定の実装は、エラーバッグとリダイレクト先を添えて検証例外を投げるだけの短いメソッド。ここを差し替えれば、独自のJSON構造を返す、エラーコード体系を持たせるといった処理を挟めます。ただし影響範囲は広く、リダイレクトや入力値の再表示の仕組みを自前実装で置き換えることに。画面側のフォームでは既定のまま使い、API向けのFormRequestだけ上書きする線引きをおすすめします。

ルールの記法とカスタムルールを実装する3つの方式と選び分けの基準

ルールの記法は文字列と配列の2通りあり、規則で表せない判定にはカスタムルールを作ります。使い捨てか再利用かで選び分けます。

パイプ区切りと配列記法のどちらを既定の記法として据えるべきか

ルールはパイプ記号で区切った1本の文字列でも、規則を1つずつ並べた配列でも書けます。短い規則を並べるだけなら文字列が読みやすいでしょう。ただし正規表現を渡す規則では、パターンの中にパイプ記号が入ると区切りとして解釈されて壊れます。カスタムルールのインスタンスやクロージャを混ぜる場合も、文字列では表現できません。

この差を毎回判断するより、配列記法を既定にしておくほうが事故は減ります。行が縦に伸びる代わりに、条件の追加時に差分が見やすくなる利点も。既存プロジェクトに手を入れる場合は、そのファイルの流儀に合わせてください。

make:ruleが生成するValidationRuleの実装形

再利用するカスタムルールはphp artisan make:ruleで生成します。生成されるのはValidationRuleインタフェースを実装したクラスで、実装するメソッドは1つだけ。属性名、値、失敗時に呼ぶクロージャを受け取り、戻り値は持ちません。判定に失敗したらクロージャにメッセージを渡して呼ぶ、という形になります。真偽値を返すのではなく、呼ぶかどうかで表現する点が独特です。

この形の利点は、1つの規則クラスで複数の失敗理由を出し分けられること。桁数が足りない場合と文字種が違う場合で別のメッセージを渡せます。空値のときにも規則を走らせたいなら、implicit()を用意して真を返してください。既定では値が空の属性に規則は実行されず、この設定を入れると空でも呼ばれます。他の項目の内容によっては必須になる、といった判定を自作する場面で要る設定です。

使い捨ての判定はクラスを作らずクロージャのまま書いて済ませる

その画面でしか使わない判定なら、クラスを作らずクロージャをルール配列に直接書けます。受け取る引数はクラス版と同じ3つ。1箇所限りの業務ルールをクラスに切り出すと、規則クラスばかりが増えて全体像が追いにくくなります。2箇所目で使う必要が出た時点で格上げする運用で十分に回るはずです。

日本語メッセージを差し替える3つの経路と優先順位の決め方の指針

既定のメッセージは英語で出ます。日本語化の方法は複数あり、どこに書くかで影響範囲が変わります。混在させると「なぜこのメッセージが出るのか」を追う羽目になるため、置き場所のルールを先に決めてください。

言語ファイルは既定で存在しないためlang:publishで取り出す

最初につまずくのがここです。現在のアプリケーション雛形には言語ファイルのディレクトリが含まれていません。直接編集したいならphp artisan lang:publishを実行し、フレームワーク内から取り出します。実行するとlang配下に英語の検証用ファイルが現れ、これを複製して日本語版を作る流れ。ファイルが見つからないと探し回る前に、まず公開コマンドを叩いてください。

customとattributesとvaluesの3階層で書き分ける

言語ファイルの中には、規則の雛形文とは別に3つのセクションがあります。customは「この項目のこの規則のときだけ」の文面、attributesは項目名の表示、valuesは選択肢の値そのものの表示。3つの役割を混ぜると重複した定義が増えていきます。

置き場所 効く範囲 使う場面
規則の雛形文 アプリ全体 文体の統一
customセクション 項目と規則の組 個別の言い回し
attributes 項目名の表示 和名への置換
FormRequestのmessages そのリクエスト 画面固有の文面

言語ファイルとFormRequestのどちらに書くかの線引き

判断基準はシンプルです。同じ項目名が複数画面に出るなら言語ファイル、その画面固有の言い回しならFormRequestのメッセージ定義に置きます。両方に書くとリクエスト側が勝つため、言語ファイルを直したのに反映されないという混乱が起きがち。項目名は言語ファイルのattributesに集約し、文面のカスタマイズは原則FormRequestへ寄せる形が扱いやすいでしょう。項目名は画面をまたいで共通、文面は画面ごとに変わりやすいという性質に合っています。

API開発でのJSONエラー応答と422をフロントとの契約にする

フロントとサーバーを分けた構成では、検証エラーの応答形式が実質的なインタフェース仕様になります。ここを曖昧にしたまま進めると、画面側でエラー表示を組み立てられません。

422で返る応答の構造とネストしたキーがフラット化される挙動

JSONを期待するリクエストで検証に失敗すると、ステータス422と共にmessageとerrorsの2つを持つ本体が返ります。messageは代表の1文、errorsは項目名をキーとして、その項目で失敗した理由の配列を並べたもの。1項目に複数の規則違反があれば、配列に複数の文が入ります。

押さえておきたいのが、ネストした入力のキーの扱いです。配列やオブジェクトの中の項目は、ドットでつないだ1本のキーへ潰されます。フロント側でキーを組み立てて表示位置を決める設計にしておけば、項目が増えても実装を変えずに済みます。

リダイレクトとJSONが切り替わる条件をチーム内で明示しておく

この2つの応答は、リクエスト側の期待によって自動で切り替わります。ブラウザからの通常送信ならリダイレクト、JSONを期待するヘッダが付いていれば422。開発中に「なぜかHTMLが返ってくる」という現象が起きたら、まずリクエスト側のヘッダを疑ってください。テストコードで検証エラーを確かめる場合も同じで、JSONを期待する形で叩かないとリダイレクトが返ります。

応答の構造をフロントと共有するなら、仕様書に文章で書くより機械可読な形にしておくほうが齟齬は減るはずです。JSON Schemaで構造を定義する書き方を使えば、エラー応答の形をフロントのテストからも検証できます。複数のFormRequestで応答形式を揃えたい場合は、個々のクラスを上書きするより例外ハンドラ側で検証例外を捕まえるほうが管理しやすいでしょう。その集約先の書き方はLaravel APIの作り方をまとめた記事で扱っています。

未知フィールドの拒否と入力検証をどこまで書くかを決める判断基準

ここからは設計の判断です。13系で入った仕組みと、あえて使わない条件を整理します。

13系のFailOnUnknownFieldsは既定オフで挙動が変わる

13.xのFormRequestには、ルールに書かれていないキーが送られてきたら弾く仕組みが入りました。クラスにFailOnUnknownFields属性を付けるか、サービスプロバイダで静的メソッドを呼んで全体に効かせるかの2通り。ソースを読むと静的プロパティの既定値はfalseで、明示的に有効化しない限り従来どおり未知のキーは無視されます。

有効にしたときの挙動も確かめておきます。ルール配列のキーが許可リストとして扱われ、外れたキーには禁止を意味する規則のメッセージが付く仕組み。入力キーはネストを含めてドット記法へ展開されたうえで照合されるため、階層の奥に紛れ込んだキーも検出されます。エラーは通常の検証エラーと同じ集合に載るので、応答形式は変わりません。方式そのものの妥当性は許可リスト設計と防げる攻撃の範囲をまとめた記事で扱っています。

全体適用に踏み切ってよい条件と、見送るべき場面の線引きの基準

判断を言い切ります。全体適用してよいのは、リクエストの送信元を自分たちが管理していて、送るキーの集合を変えるときにサーバー側のルールも必ず同時に直せる場合。フロントとサーバーを同じチームが同じリポジトリで扱っているなら満たせます。未知キーが即エラーになるため、送信側の消し忘れや命名ゆれが公開前に見つかる効果も。

見送るべきなのは、送信元が外部にある場合です。取引先のシステムや、更新タイミングを制御できないネイティブアプリの旧版がキーを1つ足しただけで、通っていたリクエストが全滅します。段階的に入れるなら、まず内部向けの管理画面のFormRequestに属性を付け、誤検知が出ないことを確かめてから広げてください。

FormRequestに寄せない判断が成立する条件の組み合わせ

最後に逆方向の判断です。認可がミドルウェアで完結していて、相関チェックが無く、応答も既定のままでよいアクションなら、コントローラ内のvalidateで十分。検索条件を受け取るだけのGETリクエストや、管理用の単純な更新がこれにあたります。クラスを増やすほど、ルールの所在を探す時間は増えていきます。

もう一つ、1つのFormRequestを複数のアクションで使い回すのも見送りの対象。作成と更新でルールが微妙に違う場合に条件分岐を書き始めると、クラスの中が「どの経路で来たか」の判定で埋まります。分けたほうが読めるでしょう。共通部分は配列を返すメソッドに切り出して両方から呼べば、重複も抑えられます。

入力検証の設計は、要件定義の段階で項目の取りうる値をどこまで詰めたかに左右されます。業務システムでこの部分が固まらないまま実装に入ると、規則の書き換えが延々と続きがち。一創では業務用・Webアプリ開発として、項目定義と検証仕様の整理から実装・保守までを一貫して請け負っています。既存システムの検証漏れの棚卸しからでも相談を受け付けています。

よくある質問

判断が分かれやすい点を、5つの質問に絞って整理します。

validateとFormRequestはどちらを使うべきですか?

まずコントローラ内のvalidateで書き始め、認可判定・相関チェック・応答の作り替えのどれかが必要になった時点でFormRequestへ移す順序をおすすめします。記法は同じで移行のコストは小さく、最初から全アクションでクラスを作ると単純な処理までファイルが分散します。再利用の有無ではなく責務で決めてください。

エラーメッセージを日本語にするにはどうすればよいですか?

言語ファイルを編集する方法と、FormRequestのメッセージ定義に書く方法があります。現在のアプリ雛形には言語ファイルのディレクトリが含まれていないため、まずphp artisan lang:publishで取り出してください。項目名だけを和名にしたいなら、attributesセクションで項目名を差し替えるほうが保守は軽く済みます。

ルールに無いキーが送られてきたときは弾けますか?

13系のFormRequestなら弾けます。クラスにFailOnUnknownFields属性を付けるか、サービスプロバイダで全体に効かせると、ルール配列のキーを許可リストとして照合する挙動。既定は無効で、有効化しない限り未知のキーは無視されて返り値から落ちるだけです。送信元を管理できない相手がいる場合、有効化は慎重に判断してください。

APIで検証エラーが起きたとき、どんな応答が返りますか?

JSONを期待するリクエストならステータス422で、代表の1文を持つmessageと、項目ごとの理由の配列を持つerrorsが返ります。ネストした入力のキーはドットでつないだ形に潰される仕組み。通常のブラウザ送信では代わりにリダイレクトするため、テストで422を確かめたい場合はJSONを期待する形で組み立ててください。

カスタムルールはクラスとクロージャのどちらで書くべきですか?

1箇所でしか使わない判定はクロージャで足ります。2箇所目で必要になった時点でphp artisan make:ruleのクラスへ格上げしてください。クラス版はValidationRuleインタフェースを実装し、失敗時に受け取ったクロージャを呼ぶ形なので、判定ロジックはそのまま移せます。使い捨ての規則までクラス化すると、ファイルが増えて全体像が追いにくくなります。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.09 テックブログ IDCFクラウド(IDCフロンティア)不正アクセス・ランサムウェア:影響先・復旧・データは戻るか
  2. 2026.10.09 テックブログ ユニリタの不正アクセス:SaaS文書管理から漏れた顧客の認証情報と回す手順
  3. 2026.10.09 テックブログ ニッスイのサイバー攻撃で日水物流の入出荷停止|委託先クラウド障害に荷主が備える手順
  4. 2026.10.08 テックブログ 大阪公立大学のランサムウェア被害と仮想化基盤の停止|全授業休講に至った経緯とバックアップを守る設定
  5. 2026.10.09 テックブログ 京王電鉄のランサムウェア被害とグループ共通基盤:決済・ポイント・予約が止まった範囲と遮断の初動

RELATED POSTS 関連記事

目次