Laravel Validationの使い方完全ガイド|基本ルール・FormRequest・カスタム検証まで徹底解説

Laravel入門

LaravelでフォームやAPIの入力データを扱う際、必須となるのがバリデーション(入力値検証)です。不正な値や空データの登録を防ぎ、アプリケーションの安全性とデータ整合性を保つために欠かせない機能です。

Laravelには非常に強力で柔軟なバリデーション機能が標準搭載されており、シンプルな単一ルールのチェックから、複雑な条件付き検証・配列データの検証・カスタムルールの作成まで、少ないコードで宣言的に実装できます。

本記事では、Laravel 10 / 11 / 12 に対応したLaravel Validationの基本から実践的な応用テクニックまでを、実際に動作するコード例とともにわかりやすく解説します。

  1. Laravelバリデーションの基本概念
  2. Laravel Validation 3つの実装手法と使い分け
    1. ① 最も手軽な「$request->validate()」
    2. ② 本番開発のデファクト「FormRequest」
    3. ③ 挙動を細かく制御できる「Validator::make()」
  3. 主要バリデーションルール一覧(目的別リファレンス)
  4. 配列データ・ネスト構造のバリデーション
    1. ワイルドカード(*)を使った配列要素の検証
    2. 配列要素へのカスタムメッセージ指定
  5. 条件付きバリデーションの実装方法
    1. ① Rule::requiredIf による条件付き必須チェック
    2. ② Rule::when による複数ルールの動的切り替え
    3. ③ FormRequestのafterフックを使った相関チェック
  6. データベースのユニーク制約(新規作成 vs 更新時の除外)
  7. エラーメッセージの表示とBlade・old()連携
    1. バリデーションメッセージの日本語化
  8. カスタムバリデーションルールの作成方法
    1. ① make:rule コマンドでルールクラスを生成
    2. ② ValidationRule インターフェースの実装
    3. ③ ルールクラスの使用
    4. ④ 単発ならクロージャでも実装可能
  9. API開発・SPAにおけるバリデーションと422エラー
  10. よくある質問(FAQ)とトラブルシューティング
    1. Q. nullable を指定しているのに必須エラーになります
    2. Q. チェックボックスが未選択のときにバリデーションが通りません
    3. Q. バリデーション通過後のデータを安全に取得するには?
  11. まとめ
  12. 関連記事

Laravelバリデーションの基本概念

バリデーションとは、クライアント(ブラウザやAPIクライアント)から送信されたリクエストパラメータが、想定通りの型・形式・値の範囲・一意性を満たしているかをチェックする処理です。

もしバリデーションでエラーが発生した場合、Laravelは次のように自動で適切なレスポンスを返します:

  • 通常のWebフォーム(HTMLリクエスト):直前のページへ自動リダイレクトし、エラーメッセージ($errors)と直前の入力値(old())をセッションに保持します。
  • API / 非同期通信(JSONリクエスト):HTTPステータスコード 422 Unprocessable Entity とエラー内容を含むJSONレスポンスを即座に返します。

Laravel Validation 3つの実装手法と使い分け

Laravelでバリデーションを実装する方法は主に3つあります。規模や用途に合わせて最適な方法を選択しましょう。

実装方法 概要 主な用途・向いている場面
1. $request->validate() コントローラのメソッド内で直接検証 小規模なフォーム、単発の処理、手軽に実装したいとき
2. FormRequest 専用のリクエストクラスに分離 本番開発の標準。ルールが複雑、再利用したい、認可(Policy/Gate)も含めたいとき
3. Validator::make() ファサードを使って手動でバリデータを生成 失敗時の処理を独自にカスタマイズしたいとき、バッチ処理や配列・文字列の手動検証

① 最も手軽な「$request->validate()」

コントローラ内で最もシンプルに書ける方法です。検証に失敗すると自動的に直前の画面へリダイレクト(または422 JSON応答)され、以降の処理は実行されません。

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function store(Request $request)
    {
        // バリデーションルールの定義
        $validated = $request->validate([
            'title'   => 'required|string|max:255',
            'content' => 'required|string|min:10',
            'status'  => 'required|in:draft,published',
        ]);

        // バリデーションを通過した安全なデータのみを取得して保存
        $post = Post::create($validated);

        return redirect()->route('posts.show', $post)
            ->with('success', '記事を作成しました。');
    }
}

② 本番開発のデファクト「FormRequest」

バリデーションルールや独自エラーメッセージ、認可ロジックをコントローラから独立した専用クラスへ切り出す手法です。コントローラが肥大化するのを防ぎ、保守性が大幅に向上します。

まずはArtisanコマンドでFormRequestクラスを生成します:

php artisan make:request StorePostRequest

生成された app/Http/Requests/StorePostRequest.php を編集します:

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StorePostRequest extends FormRequest
{
    /**
     * リクエストの実行権限があるかを判定
     */
    public function authorize(): bool
    {
        return true; // 認証・権限チェックを行わない場合はtrue
    }

    /**
     * バリデーションルール
     */
    public function rules(): array
    {
        return [
            'title'   => ['required', 'string', 'max:255'],
            'content' => ['required', 'string', 'min:10'],
            'status'  => ['required', 'in:draft,published'],
        ];
    }

    /**
     * カスタムエラーメッセージ(任意)
     */
    public function messages(): array
    {
        return [
            'title.required'   => 'タイトルは必ず入力してください。',
            'content.min'      => '本文は10文字以上で入力してください。',
        ];
    }
}

コントローラでは、メソッドの引数に型宣言するだけで自動的にバリデーションが実行されます:

use App\Http\Requests\StorePostRequest;
use App\Models\Post;

public function store(StorePostRequest $request)
{
    // メソッド内部に入った時点で検証は成功している
    $validated = $request->validated();

    $post = Post::create($validated);

    return redirect()->route('posts.show', $post);
}

FormRequestのより詳しい使い方や高度なカスタマイズ方法は、Laravel FormRequestを活用したバリデーションの効率化ガイドでも解説しています。

③ 挙動を細かく制御できる「Validator::make()」

手動でバリデータインスタンスを生成することで、エラー時のリダイレクト先を個別に指定したり、バッチ処理やサービス層で任意の配列データを検証できます。

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

public function customValidate(Request $request)
{
    $validator = Validator::make($request->all(), [
        'name'  => 'required|string|max:50',
        'email' => 'required|email',
    ]);

    // 検証失敗時の個別ハンドリング
    if ($validator->fails()) {
        return redirect('custom-form')
            ->withErrors($validator)
            ->withInput();
    }

    // 検証済みデータの取得
    $validated = $validator->validated();
}

主要バリデーションルール一覧(目的別リファレンス)

Laravelには標準で豊富なバリデーションルールが用意されています。ルールは 'required|string|max:255' のようにパイプ(|)で連結するか、['required', 'string', 'max:255'] のように配列形式で指定します。

分類 ルール 役割・説明
基本・存在 required 必須項目。null、空文字、空配列を不合格とする
nullable nullや空文字を許容する(指定がない場合は他ルールのチェックをスキップ)
sometimes そのフィールドがリクエスト内に存在する場合のみ検証する
文字列・形式 email 有効なメールアドレス形式であるかを検証
url 有効なURL形式(https://… など)であるかを検証
string / alpha / alpha_num 文字列型、半角英字のみ、半角英数字のみであるかを検証
confirmed フィールド名_confirmation(例: password_confirmation)と値が一致するか検証
数値・範囲・桁数 numeric / integer 数値、または整数であることを検証
between:min,max 値の大きさ(数値)、文字数(文字列)、要素数(配列)が範囲内かを検証
min:val / max:val 最小値/最大値(数値)、最小/最大文字数(文字列)を検証
digits:n / digits_between:min,max 正確に指定された桁数、または指定桁数範囲の数値かを検証
min_digits:n / max_digits:n 数値の最小桁数、最大桁数を検証
真偽・同意 boolean true, false, 1, 0, “1”, “0” のいずれかであることを検証
accepted / declined 利用規約への同意(yes, on, 1, true)または拒否を検証
ファイル・画像 file / image アップロードが成功したファイル、または画像ファイル(jpg, png, webpなど)かを検証
mimes:jpg,png,pdf 指定されたMIMEタイプ・拡張子に合致するかを検証
extensions:jpg,png ユーザーが指定したファイル拡張子を厳格に検証
dimensions 画像の幅・高さ・アスペクト比(例: min_width=100,max_height=500)を検証
日付・時刻 date / date_format:Y-m-d 有効な日付であるか、指定の日付フォーマットに合致するかを検証
after:date / before:date 指定した日付より後(未来)、または前(過去)であるかを検証
データベース unique:table,column 指定テーブルの特定カラムで値が重複していないか(一意)を検証
exists:table,column 指定テーブルの特定カラムに該当するレコードが存在するかを検証
ネットワーク ip / ipv4 / ipv6 有効なIPアドレス形式であるかを検証

配列データ・ネスト構造のバリデーション

動的に増減するフォームやJSON APIでは、配列やネストされたオブジェクトを受け取ることが多々あります。Laravelではワイルドカード(*)を使って簡単に配列要素を検証できます。

ワイルドカード(*)を使った配列要素の検証

$request->validate([
    // 配列自体の検証(必須かつ1件以上5件以下)
    'cart_items'            => 'required|array|min:1|max:5',

    // 配列内の各要素に対する検証
    'cart_items.*.id'       => 'required|integer|exists:products,id',
    'cart_items.*.quantity' => 'required|integer|min:1|max:99',
    'cart_items.*.options'  => 'nullable|array',
]);

配列要素へのカスタムメッセージ指定

ワイルドカードで指定したフィールドにも、個別のエラーメッセージを定義できます:

public function messages(): array
{
    return [
        'cart_items.min' => 'カートには最低1点以上の商品を入れてください。',
        'cart_items.*.quantity.min' => '注文数量は1個以上を指定してください。',
    ];
}

条件付きバリデーションの実装方法

「チェックボックスがONのときだけ追加入力を必須にする」「管理者権限のときだけ特定のルールを適用する」といった動的な条件付き検証も、Laravelならスマートに記述できます。

① Rule::requiredIf による条件付き必須チェック

クロージャまたはブール値を渡すことで、条件に合致したときだけ required を適用できます。

use Illuminate\Validation\Rule;

$request->validate([
    'is_company'   => 'required|boolean',
    'company_name' => [
        Rule::requiredIf(fn () => $request->boolean('is_company')),
        'nullable',
        'string',
        'max:100',
    ],
]);

条件付き同意チェックには accepted_ifdeclined_if も活用できます。

② Rule::when による複数ルールの動的切り替え

条件に応じて複数のバリデーションルールを一括で追加したい場合は Rule::when() が便利です:

use Illuminate\Validation\Rule;

$request->validate([
    'payment_method' => 'required|in:credit_card,bank_transfer',
    'card_number'    => Rule::when(
        $request->input('payment_method') === 'credit_card',
        ['required', 'numeric', 'digits_between:14,16'],
        ['nullable']
    ),
]);

③ FormRequestのafterフックを使った相関チェック

「項目Aと項目Bの合計が100以下であること」のような複数フィールドにまたがる複雑な検証は、FormRequestの after() メソッドで記述します。

use Illuminate\Validation\Validator;

public function after(): array
{
    return [
        function (Validator $validator) {
            if ($this->discount_amount > $this->total_price) {
                $validator->errors()->add(
                    'discount_amount',
                    '割引額が合計金額を超えることはできません。'
                );
            }
        }
    ];
}

データベースのユニーク制約(新規作成 vs 更新時の除外)

ユーザー登録時のメールアドレス重複チェックなどで使う unique ルールですが、ユーザー情報更新(編集)画面では注意が必要です。更新時にも単純に unique:users,email を指定すると、自分自身のメールアドレスで重複エラーが発生してしまいます。

更新時は Rule::unique()->ignore() を使って、現在ログイン中のユーザーや更新対象モデルのIDを除外します:

use Illuminate\Validation\Rule;

public function update(Request $request, User $user)
{
    $validated = $request->validate([
        'name'  => ['required', 'string', 'max:255'],
        'email' => [
            'required',
            'email',
            'max:255',
            // 自分自身のIDを除外して重複チェック
            Rule::unique('users', 'email')->ignore($user->id),
        ],
    ]);

    $user->update($validated);

    return redirect()->route('users.show', $user);
}

エラーメッセージの表示とBlade・old()連携

バリデーションエラーが発生した場合、Bladeテンプレートでは $errors オブジェクトや @error ディレクティブを使ってエラーメッセージを描画します。また、直前の入力内容を復元するために old() ヘルパー を併用します。

{{-- 全エラーメッセージの一括表示 --}}
@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

<form action="{{ route('posts.store') }}" method="POST">
    @csrf

    <div class="form-group">
        <label for="title">タイトル</label>
        <input type="text" name="title" id="title"
               value="{{ old('title', $post->title ?? '') }}"
               class="@error('title') is-invalid @enderror">
        @error('title')
            <div class="error-message">{{ $message }}</div>
        @enderror
    </div>

    <button type="submit">保存</button>
</form>

バリデーションメッセージの日本語化

Laravelのデフォルトエラーメッセージは英語です。日本語化するには、言語ファイルを公開して設定を変更します:

php artisan lang:publish

lang/ja/validation.php を作成して日本語翻訳を定義するか、コミュニティパッケージ(laravel-lang/common 等)を導入します。詳しい手順は Laravelの日本語化ステップバイステップガイドlang:publish コマンド解説 を参照してください。

カスタムバリデーションルールの作成方法

標準ルールにない特殊な検証(郵便番号の形式、全角カタカナチェック、外部APIとの照合など)を行いたい場合は、独自のカスタムルールクラスを作成します。

① make:rule コマンドでルールクラスを生成

php artisan make:rule PostalCode

コマンドのオプション詳細は make:rule コマンド解説 を参照してください。

② ValidationRule インターフェースの実装

Laravel 10 / 11 / 12 では、Illuminate\Contracts\Validation\ValidationRule インターフェースを実装し、validate() メソッド内に検証ロジックを記述します。

<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class PostalCode implements ValidationRule
{
    /**
     * バリデーションの実行
     *
     * @param  string  $attribute 属性名
     * @param  mixed   $value     入力値
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // ハイフンありの郵便番号形式 (例: 123-4567) を正規表現でチェック
        if (! preg_match('/^\d{3}-\d{4}$/', (string) $value)) {
            $fail(':attribute は「123-4567」の形式で入力してください。');
        }
    }
}

③ ルールクラスの使用

use App\Rules\PostalCode;

$request->validate([
    'postal_code' => ['required', 'string', new PostalCode],
]);

④ 単発ならクロージャでも実装可能

クラスを作成するほどではない簡易チェックであれば、インラインのクロージャでも同様に検証できます:

$request->validate([
    'code' => [
        'required',
        function (string $attribute, mixed $value, Closure $fail) {
            if ($value === 'invalid_code') {
                $fail(':attribute に無効なコードが指定されました。');
            }
        },
    ],
]);

API開発・SPAにおけるバリデーションと422エラー

Vue.js、React、Next.jsなどのSPAやモバイルアプリ向けのAPI開発では、リクエストヘッダーに Accept: application/json を付与してリクエストを送信します。

LaravelはリクエストがJSONを期待していることを自動検知し、バリデーション失敗時にリダイレクトせず、以下のような HTTP 422 (Unprocessable Entity) のJSONレスポンスを返します:

{
    "message": "The given data was invalid.",
    "errors": {
        "title": [
            "タイトルは必ず入力してください。"
        ],
        "email": [
            "有効なメールアドレスを入力してください。"
        ]
    }
}

フロントエンド側では、response.data.errors を参照して各フォーム要素の下にエラーメッセージを動的に表示できます。

よくある質問(FAQ)とトラブルシューティング

Q. nullable を指定しているのに必須エラーになります

A. ルールの記述順序や required との組み合わせを確認してください。'required|nullable' と書くと required が優先されてしまいます。任意入力にしたい項目は 'nullable|string|max:100' のように required を外して nullable を先頭に配置しましょう。

Q. チェックボックスが未選択のときにバリデーションが通りません

A. HTMLのチェックボックスは、未チェック時にリクエストパラメータ自体が送信されません。未チェック状態でもキーを渡したい場合は、hidden フィールドでデフォルト値(0など)を事前に送信するか、$request->boolean('agree')Rule::requiredIf を活用してください。

Q. バリデーション通過後のデータを安全に取得するには?

A. $request->all() を使わず、必ず $request->validated()(または $validated = $request->validate(...) の戻り値)を使用してください。定義したルールにマッチした検証済みキーのみが抽出されるため、意図しないパラメータのマスアサインメントを防ぐことができます。

まとめ

Laravel Validationは、開発規模や要件に合わせて柔軟に使い分けられる完成度の高い検証機能です。

  • 手軽に済ませたい場合は $request->validate()
  • 保守性や再利用性を高めたい本番アプリでは FormRequest
  • 特殊な制御を行いたい場合は Validator::make()ValidationRule カスタムクラス

バリデーション通過後の安全なデータ($request->validated())をもとに動的なクエリやコレクションを組み立てる際は、Laravelのwhenメソッドの使い方|条件分岐をスマートに書くテクニック を活用すると、検索条件に応じた絞り込みや除外をスマートに実現できます。

適切なバリデーションを設計し、堅牢で安全なLaravelアプリケーションを構築しましょう。

レン (Wren)

こんにちは。レンです。

Laravelのコードの森に住んでいる、小さな案内役です。
ルーティングの枝やクラスの影を歩きながら、コードの流れや仕組みを眺めています。

このサイトでは、Laravelの基本から実装のコツまで、開発で役立つポイントを静かに整理しています。
難しいことを増やすのではなく、コードの見通しが少し良くなるヒントを届けるのが役目です。

「この処理はどこに書くのがいいのか」
「Laravelではどう考えると整理できるのか」

そんな疑問に、小さなメモを残すような気持ちで記事を書いています。

コードを書いている途中で迷ったとき、
このサイトが少し立ち止まって整理できる場所になればうれしいです。

レン (Wren)をフォローする

コメント