🤖

Laravel Tips集 ~Validationの便利技大全~

2025/02/14に公開

Laravel Validation Tips チートシート

バリデーションルールやフォームリクエストのカスタマイズ方法、エラーメッセージの改善など、バリデーションに関するさまざまなテクニックをまとめました。


1. インラインバリデーション

通常はカスタムクラスで定義するバリデーションルールも、クロージャを利用してインラインで記述できます。
※メールアドレスの末尾が @example.com でなければエラーとする例

<?php

request()->validate([
    'email' => [
        'required',
        'email',
        function ($attribute, $value, $fail) {
            if (substr($value, -12) !== '@example.com') {
                $fail($attribute, 'The email must belong to example.com domain');
            }
        },
    ],
]);

2. ネストされた配列のバリデーション

Laravel 9 以降では、Rule::forEach() を利用することで、ネストされた配列内の各要素に対して個別のバリデーションルールを定義できます。

<?php

use App\Rules\HasPermission;
use Illuminate\Validation\Rule;
use Illuminate\Support\Facades\Validator;

$validator = Validator::make($request->all(), [
    'companies.*.id' => Rule::forEach(function (string|null $value, string $attribute) {
        return [
            Rule::exists(Company::class, 'id'),
            new HasPermission('manage-company', $value),
        ];
    }),
]);

3. 最初の失敗で停止する

フォームリクエストクラスのプロパティに stopOnFirstFailure を true に設定すると、最初のバリデーションエラーで残りのチェックをスキップできます。

<?php

/**
 * Indicates if the validator should stop on the first rule failure.
 *
 * @var bool
 */
protected $stopOnFirstFailure = true;

4. bail ルール

フィールドごとに「bail」を指定することで、最初のルールに失敗した時点で以降のルールチェックを中断します。

<?php

$request->validate([
    'title' => 'bail|required|unique:posts|max:255',
    'body' => 'required',
]);

5. バリデーション済み入力の抽出

リクエストでバリデーション済みの値を、safe() メソッドを利用して簡単に取得できます。
※必要なキーだけ抽出や除外が可能

<?php

$validated = $request->safe()->only(['name', 'email']);
$validated = $request->safe()->except(['name', 'email']);
$validated = $request->safe()->all();

6. 条件付きルールの追加

動的なフォームの場合、ある入力値がチェックされている場合のみ特定のバリデーションを適用したい時は、exclude_if ルールを使います。

<?php

use Illuminate\Support\Facades\Validator;

$validator = Validator::make($data, [
    'has_appointment' => 'required|boolean',
    'appointment_date' => 'exclude_if:has_appointment,false|required|date',
    'doctor_name' => 'exclude_if:has_appointment,false|required|string',
]);

7. 正規化処理(Normalize Validated Data)

フォームリクエストの passedValidation フックを利用することで、バリデーション後にデータを整形できます。
※例:名前を先頭大文字に変換

<?php

protected function passedValidation(): void
{
    $this->replace([
        'name' => ucwords(strtolower($this->name)),
    ]);
}

8. 日付バリデーションの簡潔な記述

バリデーションルールで「today」「tomorrow」などの文字列を利用できるため、日付の条件が読みやすくなります。

<?php

$rules = [
    'start_date' => 'required|date|after:tomorrow',
    'end_date' => 'required|date|after_or_equal:start_date',
    'past_date' => 'required|date|before:yesterday',
    'deadline' => 'required|date|before_or_equal:today',
];

9. 入力の除外(Exclude Validated Input)

特定のフィールドをバリデーション済み配列から除外したい場合は、exclude ルールを利用します。

<?php

public function store(Request $request): RedirectResponse
{
    $validated = $request->validate([
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'captcha' => 'required|exclude',
    ]);

    dd($validated); // 'title' と 'body' のみが含まれる
}

10. 実在するメールアドレスのチェック

dns ルールを利用して、存在するメールサーバーのあるメールアドレスかどうかチェックできます。
※テスト環境では freeEmail() を利用することを推奨

<?php

public function store(Request $request): RedirectResponse
{
    $validated = $request->validate([
        'email' => 'required|email:dns'
    ]);
}

11. 配列のエラーメッセージ改善

配列の各アイテムでエラーが発生した場合、:index:position プレースホルダを利用して、具体的な位置を示すエラーメッセージを表示できます。

<?php

use Illuminate\Support\Facades\Validator;

$validator = Validator::make($request->all(), [
    'photos.*.description' => 'required',
], [
    'photos.*.description.required' => 'Please describe photo #:position.',
]);

12. Required If Accepted ルール

あるチェックボックスが選択されている場合のみ、別のフィールドを必須にするために、required_if_accepted ルールを利用できます。

<?php

$validator = Validator::make($data, [
    'subscribe_to_newsletter' => 'boolean',
    'email' => 'required_if_accepted:subscribe_to_newsletter|email',
]);

13. 画像のサイズ検証

画像ファイルの幅、高さ、アスペクト比などを検証する場合、dimensions ルールを利用して簡潔に定義できます。

<?php

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

Validator::make($data, [
    'avatar' => [
        'required',
        Rule::dimensions()->maxWidth(1000)->maxHeight(500)->ratio(3 / 2),
    ],
]);

14. prohibitedIf ルール

条件に応じて、特定のフィールドに値が入っていること自体を禁止する場合は、prohibitedIf ルールを利用します。

<?php

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
 
// 管理者の場合、role_id の入力を禁止する
Validator::make($request->all(), [
    'role_id' => Rule::prohibitedIf($request->user()->is_admin),
]);

15. sometimes ルール

フィールドが存在する場合のみバリデーションを行い、存在しない場合はスキップするには、sometimes ルールを使用します。

<?php

$validator = Validator::make($data, [
    'email' => ['sometimes', 'email'],
]);

16. distinct ルール

配列内に重複する値がないかチェックするには、distinct ルールを利用します。厳密な比較(型も考慮)も可能です。

<?php

use Illuminate\Support\Facades\Validator;

$posts = [
    ['title' => 'First Post'],
    ['title' => 'Second Post'],
    ['title' => 'First Post'], // 重複あり
    ['title' => 'Third Post'],
];

Validator::make($posts, ['*.title' => 'distinct:strict'])->fails(); // true

17. 現在のパスワード確認

アカウント削除などの際、ユーザーに現在のパスワードの確認を求める場合、current_password ルールを利用するとシンプルです。

<?php

public function destroy(Request $request): RedirectResponse
{
    $request->validate([
        'password' => ['required', 'current_password:web'],
    ]);
 
    // アカウント削除処理...
 
    return to_route('home');
}

18. 条件付きバリデーション

特定の条件下でのみルールを適用したい場合、Validator インスタンスの sometimes メソッドで柔軟に設定できます。

<?php

use Illuminate\Support\Fluent;
use Illuminate\Support\Facades\Validator;
 
$validator = Validator::make($request->all(), [
    'email' => 'required|email',
    'games' => 'required|numeric',
]);

// games の数が 100 以上なら reason フィールドに必須・最大 500 文字のルールを適用
$validator->sometimes('reason', 'required|max:500', function (Fluent $input) {
    return $input->games >= 100;
});

19. デフォルトのパスワードルールのカスタマイズ

Laravel のデフォルトパスワードルールを、環境に応じてカスタマイズすることで、コード全体で一貫性を保つことができます。

<?php

use Illuminate\Validation\Rules\Password;

public function boot(): void
{
    Password::defaults(function () {
        $rule = Password::min(8);

        return $this->app->isProduction()
            ? $rule->mixedCase()->uncompromised()
            : $rule;
    });
}

// 利用例
'password' => ['required', Password::defaults()]

元記事

https://github.com/OussamaMater/Laravel-Tips/blob/main/tips/validation.md?plain=1

Discussion