【Laravel】カスタムバリデーションでハマった話

Laravel

Laravelでルールオブジェクトを使ったカスタムバリデーションを実装したときにハマったポイントをまとめておきます。

ルールオブジェクトを使ったカスタムバリデーション

Laravelでは標準で数多くのバリデーションルールが用意されており簡単に利用することができます。

フォームリクエストでの標準バリデーションの例↓

public function rules()
{
    return [
        'name' => ['required', 'string', 'max:255'],
        'email' => ['required', 'email', 'max:255'],
    ];
}

しかし、時には標準のバリデーションルールではカバーしきれない場合もあり、独自ルールを作ってバリデーションを実行したいこともあると思います。

そんな時にルールオブジェクトを使った方法が有効的です。

Illuminate\Contracts\Validation\Ruleインターフェイスを実装することで実現できます。

下記を実行するとapp/Rulesディレクトリ配下にHogeRule.phpが作成されます。

php artisan make:rule HogeRule

作成されたルールクラスのpassesメソッドの中がバリデーションの内容になります。

事象

独自ルールを実装したときに下記のような問題に直面しました。

nullableな項目でその値が何かの値と一致しているかを確認したい場合にバリデーションが実行されない

例を出すと、Aの場合はNULLを許容するが、それ以外の場合は値がBと一致している必要があるみたいな場合です。

コードで書くとこのような感じ↓

/**
* Determine if the validation rule passes.
*
* @param  string  $attribute
* @param  mixed  $value
* @return bool
*/
public function passes($attribute, $value)
{
    $now = CarbonImmutable::now()->hour;
    if ($value === null && $now > 12 ) {
        return true;
    }
    return $value === 'hoge';
}

現在時刻が午後(12時以降)であれば$valueがNULLであることを許容し、それ以外は「hoge」と一致している必要があるというルールですね。

しかし、このルールを適応して午前中にNULLを送ったとしてもバリデーションで弾かれず処理が通ってしまいます。

原因

公式ドキュメントにも書いてあるのですが、デフォルトではその項目が存在しない場合や空文字列の場合はバリデーションが実行されない仕様になっているためでした。

デフォルトでは、バリデーションされる属性が存在しないか、空の文字列が含まれている場合、カスタムルールを含む通常のバリデーションルールは実行されません。

https://readouble.com/laravel/8.x/ja/validation.html#implicit-rules

試しに先程のコードにdd()を追加して処理を止めようとしましたが実行されませんでした。

/**
* Determine if the validation rule passes.
*
* @param  string  $attribute
* @param  mixed  $value
* @return bool
*/
public function passes($attribute, $value)
{
    $now = CarbonImmutable::now()->hour;
    dd($now);
    if ($value === null && $now > 12 ) {
        return true;
    }
    return $value === 'hoge';
}

解決策

結論から言うとRuleの代わりにImplicitRuleインターフェイスを実装することで値が存在しなかったり空文字列だったとしてもルールオブジェクトによるバリデーションが実行されるようになります。

ImplicitRuleRuleを継承しているのでルールの処理自体は同じで問題なく、ただ実装するインターフェイスを変更するだけです。

<?php

namespace Illuminate\Contracts\Validation;

interface ImplicitRule extends Rule
{
    //
}

implicitという単語の意味から暗黙のルールと呼ばれ、暗黙的に属性が必須であることにします。

そのため、NULLの場合にどう処理するかは実装者次第となります。

ImplicitRuleを実装したルールオブジェクトの挙動がこちらです↓

無事バリデーションが実行されました。

コメント

タイトルとURLをコピーしました