カテゴリー
コンピューター

Laravel で「Parse, don’t validate」を理解し実践するための思考と設計 – Laravel で FormRequest を境界として扱い、Parse, don’t validate の本質を理解して実践するためのガイド。設計の背景やテスト戦略、app/Data 配置の理由まで。

はじめに(この記事について)

この記事は、OpenAI が開発した生成AIモデル ChatGPT (GPT-5 ?) によって作成された原稿をもとに執筆しています。
私(著者)は、生成AIと対話しながら考えを深め、整理し、Laravel での実装に落とし込んでいく過程をそのままブログ記事としてまとめました。

なぜこのテーマに取り組んだのか(出発点の疑問)

私は、最近まで Laravel の入力処理は 基本的に FormRequest とバリデーションルール(rules) で済ませてきました。
その中で、

  • FormRequest でバリデーションしたあとに Controller で validated() を使って値を取り出す
  • その値はプリミティブな配列や文字列であることが多い
  • これで本当に安全で、きれいに扱われているのか

というモヤモヤをずっと抱えていました。

そのとき目にしたのが、PHPカンファレンス福岡 2025 での twada さんの講演です。
講演タイトルは以下のスライドにあります。

私は現地参加ではなく、講演動画で視聴しました

視聴した動画はこちらです。

講演の中で 「Parse, don’t validate」 というフレーズが出てきた、そして、どこかに「just」がついていた気がする、どこだったろうか、という軽い問いから探索が始まりました。

「Parse, don’t validate」はソフトウェア設計の文脈でよく語られるフレーズで、元となるブログ記事が次の URL にあります。

生成AIとやり取り始めた時に、別途検索して読んでみたけれど、その時はよくわからなかったです。。。

Parse, don’t validate とは何か(設計の本質)

生成AIとやり取りして、こんな感じとわかった。

「Parse, don’t validate」の本質は、以下のように言われています。

  • 単にバリデーション(validate)するだけで止めない
  • バリデートした値を 意味のある構造や型(型付きの値)として扱う
  • その値が正しいことを型そのものによって保証する

つまり、

入力データを単に検査するのではなく、型や構造に変換して扱い、その構造の段階で安全性を担保する

という考え方です。

表面的には「validate したあとに DTO に詰め替えること?」と思いがちですが、実際にはもっと根源的な意味として、

バリデーションという真偽判定ではなく、意味のある値として扱うこと

が大事です。

Laravel の FormRequest との関係

わかったような、わからないような。。。私は Laravel をよく使っているので、バリデーションといえば FormRequest だろうということで、どんな感じで実現できるかを生成AIと一緒に考えました。

Laravel では FormRequest を使ってバリデーション仕様(rules)を定義し、 Controller のアクション内で validated()$request->input() を使って値を取り出します。

しかしこれには次の問題がありました。

  • validated() は配列で返るだけで、値そのものに意味が付与されていない
  • Controller 側でバリデーション済みの配列に依存してしまう
  • 「安全性を境界で担保する」という意識が薄くなる

このとき私が抱いたのは次のような直感です。

FormRequest 内で、単に rules を定義するだけでなく、意味のある値を返すメソッドを定義できないか?

そしてその価値は、値を取り出す側(Controller)が 値の意味や整合性を気にしなくていい設計 にできることでした。

Laravel に落とし込むとどうなるか

具体的に私が導き出した方法は以下の通りです。

1. FormRequest は「境界」である

FormRequest は

  • HTTP からの入力
  • 内部(Controller 以降)で使う値

境界 として扱います。

そのため、

  • validated() は FormRequest 内で使う
  • Controller は FormRequest の getter を使う
  • 値の正規化や構造化は FormRequest の中で行う

という方針にしました。

2. Controller では validated() を直接使わない

Controller は次のように FormRequest のメソッドを呼びます。

$email = $request->email();

このようにすることで、

  • Controller は入力構造(配列やキー名)に依存しない
  • 値の意味が明確に伝わる
  • validated() が Controller の外側で終わる
  • 境界が明確になる

という利点を得られます。

3. 項目が多い場合はサブ構造にまとめる

フォームの項目が多い場合、一つずつの値を取り出すメソッドを書いていくと、まるで Java の Getter/Setter だけのクラスになってしまいます。これを避けるために、意味のある単位でまとめる構造を作ることにしました。

例えば住所をまとめる場合、

public function address(): AddressData
{
    return new AddressData(
        zip: $this->validated('zip'),
        prefecture: $this->validated('prefecture'),
        city: $this->validated('city'),
    );
}

このようにすることで、

  • FormRequest 内で validated() が終わる
  • 意味の塊として Controller に渡る
  • Controller 側が配列構造を知らなくて済む

という形になります。

TODO: ここはもっと発展できる余地があると思います。つまり、 getter メソッドの返却型のサブ構造を、モデルクラスとすると、コントローラがさらに薄くなるはず。ただ、そうすると、うっかり FormRequest で DB 永続化をやってしまいたくなったりしそう。もしかしたらそれもいいのかもしれないが、検討したいです。

どこに配置するか(app/Data という結論)

こうした サブ構造(値の塊)を表すクラス をどこに置くかは悩みどころでした。 Laravel 自体は明確な規約を強制しませんが、 私は次の構成が 現実的でわかりやすいと判断しました。

app/
├─ Http/
│  └─ Requests/
│     └─ StoreUserRequest.php
├─ Data/
│  ├─ AddressData.php
│  └─ ProfileData.php

ここでポイントは以下です。

  • Http/Requests はあくまで HTTP の境界に関するもの
  • その外側にある app/Data値の構造を表すもの
  • どこに何があるかが直感的に分かる

現代の Laravel 開発者にとっても、「FormRequest はここと、値のまとまりはここ」という理解は自然なので、 学習コストが低い という利点があります。

TODO: 生成AIとやりとりしているときは、これがシンプルでいいか、と思っていましたが、サブ構造は FormRequest でしか使わないにも関わらず、 FormRequest からかなり独立した場所に置くことになるので、違和感を感じ始めています。

FormRequest のテスト戦略

この方針を採用する場合、FormRequest のテストは次のように行うのが良いです。

1. FormRequest のメソッド単体テスト

これは HTTP を使わず、 Validator を使ってバリデーションとメソッドの動作だけを確認します。

$request = new StoreUserRequest();
$request->merge([
    'email' => 'test@example.com',
    'zip' => '123-4567',
]);

$validator = Validator::make(
    $request->all(),
    $request->rules()
);
$request->setValidator($validator);

$this->assertSame(
    'test@example.com',
    $request->email()
);

このようにすることで、

  • バリデーションルール
  • parse 結果
  • getter の戻り値

を個別に確認できます。

TODO: ここのコードは生成AI出力をそのまま掲載しています。個人的には $request->merge() は好きでないし、公式の Laravel ファミリーの何かのリポジトリでこのようなテストをしていたような記憶があり、その方法に合わせたいと思っています。

2. Controller の最小テスト

Controller 側のテストは、正常系だけを確認します。

$this->postJson('/users', [
    'email' => 'test@example.com',
])->assertOk();

異常系テストは FormRequest が担保します。

DDD やクリーンアーキテクチャの影響とバランス

ここまでの設計は、 DDD(ドメイン駆動設計)やクリーンアーキテクチャの考え方を 全面的に取り入れたもの ではありません。

ただし、

  • 境界を明確にする
  • 意味のある値で扱う
  • validated() は境界で終わらせる

といった点は、クリーンアーキテクチャ的な考え方と共通しています。

しかし今回の私の思い描いている大きさは 小規模プロジェクト であり、サービス層やユースケース層を無理に増やすことはしませんでした。

そのため、 最小限の構造で大きな効果を得る「実務寄りの折衷案」 として整理したものです。

おわりに

今回の探索は、

  • PHP カンファレンス福岡 2025 の講演をきっかけに
  • 「Parse, don’t validate」という言葉に引っ張られ
  • Laravel での実装に落とし込んでいった

というプロセスでした。

このプロセスでは、モヤモヤ → 疑問 → 探索 → 理解 → 実装 → ドキュメント化 という一連の思考がありましたが、その旅の結果として、

  • Laravel においても FormRequest の境界で「意味のある値」を扱う設計は実現可能
  • app/Data 配置のような現実的でわかりやすいパターンがある

という理解に至りました。いけそうな感じがします。次は、実践したいです。

この記事が、あなたの設計理解や実装の助けになれば嬉しいです。

以上です。

コメントを残す