バリデーションの話は尽きないです。過去にもいくつか取り上げました。今回もちょっと頭をひねった話です。

入力例

まずは入力例から、

ユーザーの登録の入力画面で、メルマガの購読をするならドロップダウンで1週間に受信したい回数を指定します。上のキャプチャでは見えないけれど、ドロップダウンで1回、2回、3回の選択が出てきます。メルマガの購読のチェックボックスをオフにしたら、javascriptで回数のドロップダウンの選択を無効としてもいいですね。

この画面の入力値のバリデーションとして、以下のようなコントローラのコードとします。

class DemoController extends Controller
{
    public function create(Request $request)
    {
        $options = [
            0 => '-- 選択してください --',
            1 => '1回',
            2 => '2回',
            3 => '3回',
        ];

        return view('demo2')->with(compact('options'));
    }

    public function store(Request $request)
    {
        $validated = $request->mergeIfMissing([
            'subscribe' => 'N', // メルマガ購読がオフだと値が返ってこないので、ここで値を設定
        ])->validate([
            'email'      => 'required|email',
            'subscribe'  => 'required|in:Y,N', // メルマガ購読?
            'frequency'  => 'required_if:subscribe,Y|integer|in:1,2,3', //購読するなら受信回数をチェック
        ], ['frequency.in' => '選択してください']);

        dd($validated);
    }
}

登録ボタンを押して実行してみましょう!

メルマガ購読のチェックボックスをオン(subscribe = ‘Y’)にしたら、必ず受信回数の選択が必要となり期待通りのエラーが表示されます。
上のfrequencyのルールでは、

  • required_if:subscribe,Yは、「メルマガを購読する」(subscribe = ‘Y’)がオンとなり、requiredとなりfrequencyの値(0)があるので正
  • integerでは、値(0)は整数ゆえに正
  • in:1,2,3では、値(0)は1でも2でも3でもないので誤

となりエラーです。

さて、今度はメルマガ購読をオフ(subscribe = ‘N’)にして、投稿してみます。

「メルマガを購読する」はオフなのに、どうしてこれもエラーとなるのでしょう?
先と違ってrequired_if:subscribe,Yではrequiredとなりませんが、その結果は誤でもありません。しかし最後のルールin:1,2,3では前回と同様に誤となりエラーなります。

その証拠に、今度はメルマガ購読オフで、1回を選択するとエラーとはなりません。

以前習ったbailを使っても同じです。required_if:subscribe,Yのルールの結果が誤となるわけでないので。


            'frequency'  => 'bail|required_if:subscribe,Y|integer|in:1,2,3', //購読するなら受信回数をチェック

exclude_if

一体どうすればよいのでしょうか。そこで登場するのが、exclude_ifのルールです。


            'frequency'  => 'exclude_if:subscribe,N|required_if:subscribe,Y|integer|in:1,2,3', //購読するなら受信回数をチェック

メルマガ購読がオフ(subscribe = ‘N’)なら、exclude_if:subscribe,Nで他のルールは適用されなく、以下のようにエラーとなりません。

nullable

exclude_ifがややこしいと思うなら、これはどうでしょう?

「– 選択してください –」の選択値を0でなくnullとします。
また、frequencyのルールにおいて、exclude_ifの代わりにnullableと追加します。

class DemoController extends Controller
{
    public function create(Request $request)
    {
        $options = [
            null => '-- 選択してください --', // 0でなくnullです。
            1 => '1回',
            2 => '2回',
            3 => '3回',
        ];

        return view('demo2')->with(compact('options'));
    }

    public function store(Request $request)
    {
        $validated = $request->mergeIfMissing([
            'subscribe' => 'N', // メルマガ購読がオフだと値が返ってこないので、ここで値を設定
        ])->validate([
            'email'      => 'required|email',
            'subscribe'  => 'required|in:Y,N', // メルマガ購読?
            'frequency'  => 'nullable|required_if:subscribe,Y|integer|in:1,2,3', // nullableが入っています。
        ], ['frequency.in' => '選択してください']);

        if ($validated['subscribe'] === 'N') {
            $validated['frequency'] = 0; // nullでなく0とする
        }

        dd($validated);
    }
}

匿名関数

まだややこしいと思うならこれはどうでしょう? ルールの匿名関数を使用して「メルマガを購読する」がオンのときだけ値の数字のチェックとしました。

class DemoController extends Controller
{
    public function create(Request $request)
    {
        $options = [
            0 => '-- 選択してください --',
            1 => '1回',
            2 => '2回',
            3 => '3回',
        ];

        return view('demo2')->with(compact('options'));
    }

   public function store(Request $request)
    {
        $validated = $request->mergeIfMissing([
            'subscribe' => 'N',
        ])->validate([
            'email'      => 'required|email',
            'subscribe'  => 'required|in:Y,N',
            'frequency'  => [
                'required_if:subscribe,Y',
                'integer',
                function($attribute, $value, $fail) {
                    if (request('subscribe') === 'Y') {
                        if (! in_array($value, [1,2,3])) {
                            $fail('選択してください');
                        }
                    }
                }
            ]
        ]);

        dd($validated);
    }
}

By khino