ひでメモ

プログラムについて勉強したことを書きます。たぶん。

【Laravel】Laravel のバージョンアップでバリデーションルールの微妙な仕様変更に対応した

Laravel のバージョンを 5.X => 8.X にアップデートした際に FormRequest のバリデーションルールの仕様が微妙に変わっていてテストがコケました。 2点だけですがせっかく調べたので書き残しておきます。

どこを調べたのか

ドキュメント見ても1行でさらっと書いてあるだけで細かい仕様がわからなかったのでソースコードを見ました。
以下のトレイトにvalidate<ルール名>のようなメソッドがありそれらを参照しました。
Illuminate\Validation\Concerns\ValidatesAttributes
今回は2つのメソッドしか見ませんでしたが、今後ルールの詳細な仕様が知りたいときはこちらを見ればよさそうです。

different

指定した項目と内容が異なっていることをチェックするルールです。
バリデーション 8.x Laravel

コード見れば一発ですが、以下のような違いがありました。直感的には 8.X 系のほうが自然かなと思います。

  • 5.X 系:指定した項目があり値が一致していれば NG だが、指定した項目がなくても NG
  • 8.X 系:そもそも指定した項目が存在しなければ OK。指定した項目があり値が一致していれば NG

以下はソースコードの抜粋です。もちろんコメントはわかりやすいようにこちらで入れたものです。

5.X 系

指定した項目がなければ false を返しています。

public function validateDifferent($attribute, $value, $parameters)
    {
        $this->requireParameterCount(1, $parameters, 'different');

        foreach ($parameters as $parameter) {
 // ここで指定した項目がなければ false を返しています
            if (! Arr::has($this->data, $parameter)) {
                return false;
            }

            $other = Arr::get($this->data, $parameter);

            if ($value === $other) {
                return false;
            }
        }

        return true;
    }

8.X 系

値が一致しているかの判定に入る前に指定した項目があるかをチェックして、項目がなければ一致判定すら行わず OK とするように変更されています。

public function validateDifferent($attribute, $value, $parameters)
    {
        $this->requireParameterCount(1, $parameters, 'different');

        foreach ($parameters as $parameter) {
// 値が一致しているかの判定に入る前に指定した項目があるかチェックをしていますね
            if (Arr::has($this->data, $parameter)) { 
                $other = Arr::get($this->data, $parameter);

                if ($value === $other) {
                    return false;
                }
            }
        }

        return true;
    }

required_if

指定した項目が指定した値の場合、その項目は必須とする、というルールです。
特定のフラグがオンの場合に、それに紐づく項目は必須にするというときなどに使用するかと思います。

バリデーション 8.x Laravel

   `fuga` => `required_if:hoge_flg,1`

上記のようなルールを例にすると、以下のような違いがありました。

  • 5.X 系:hoge_flgが 1・True どちらでも条件に合致したとみなす
  • 8.X 系:hoge_flgが True では条件に合致したとみなさず、1 のみを条件に合致したとみなす。

つまり、より厳密なルールになりました。

それぞれのバージョンでのvalidateRequiredIfメソッドの一部抜粋です。

// 5.X 系
        if (in_array($other, $values)) {
            return $this->validateRequired($attribute, $value);
        }


// 8.X 系
        if (in_array($other, $values, is_bool($other) || is_null($other))) {
            return $this->validateRequired($attribute, $value);
        }

両方とも条件に合致した場合は、指定した項目が存在しているかをチェックするvalidateRequiredメソッドに遷移しています。
ただ、その条件判定に使われているin_arrayで、8.X 系では第3引数も指定されています。
in_arrayの第3引数が True の場合、型の一致までチェックして配列を検索します。
この第3引数の指定の有無による違いで True はバリデーションの条件に合致しないという扱いになってしまったようです。

エラーとなったテストでは、実際は 0 or 1 がその項目に入るにも関わらず、フラグの項目だからといってテストデータで True を指定していました。
そのようなテストデータはすべて、真偽値から数値に直すことによって意図した結果になりました。
今まで適当だったテストデータがあるべき姿になったという感じですね。

感想

Laravel も PHP の型に厳密になろうぜという流れに沿って変化していってるんだな〜と思いました。。。