HTTP リクエストの入力パラメーターが2つあって、どちらも空の場合は許容しないバリデーションを実装する方法を調べてみました。
今回は p1 と p2 という GET パラメーターを受けて、両方指定がない場合はエラーを出す複合条件を作成することを目標とします。ここでは、値が空文字(?p1=
)とパラメーター指定なし( ?
)は等価です。Laravel 6.x で動作確認しました。
public function action(ExperimentalRequest $request) { return ['passes' => true]; }
コントローラーのメソッドはこんな感じで、 ExperimentalRequest
という Request クラスを実装するイメージです。(ルーティングとかは省略します)
方法1
required_without を使います。指定したパラメーターが空だった場合は必須チェックをする、という標準のバリデーションルールです。下記例では p2 が空だった場合は p1 の必須チェックを行います。
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Http\JsonResponse; use Illuminate\Contracts\Validation\Validator; class ExperimentalRequest extends FormRequest { public function rules() { return [ 'p1' => 'required_without:p2', 'p2' => '', ]; } /** * バリデーションエラーがある場合 JSON を返却する(動作確認用) */ protected function failedValidation(Validator $validator) { throw new HttpResponseException(new JsonResponse($validator->errors(), 422)); } }
required_without ルールは両方のパラメーターに指定したくなるかもしれませんが、片方に指定すれば十分です。
ただこの方法だと、両方値が設定されている場合も OK になってしまいます。どちらか一方のみにしたい場合は少し工夫が必要です。
p2 なし | p2=1 | |
p1 なし | × | ○ |
p1=1 | ○ | ○ |
? {"p1":["The p1 field is required when p2 is not present."]} ?p1=1 {"passes":true} ?p2=1 {"passes":true} ?p1=1&p2=1 {"passes":true}
方法2
両方空、両方指定ありを弾く自前のバリデーションを書いてみます。排他的論理和ですね。
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Http\Exceptions\HttpResponseException; use Illuminate\Http\JsonResponse; use Illuminate\Contracts\Validation\Validator; class ExperimentalRequest extends FormRequest { public function rules() { return []; } public function withValidator(Validator $validator) { $validator->after(function ($validator) { if ($this->filled(['p1', 'p2'])) { $validator->errors()->add( 'p1', 'p1 と p2 は両方指定できません。どちらか一方を指定してください。' ); } elseif (!$this->anyFilled(['p1', 'p2'])) { $validator->errors()->add( 'p1', 'p1 と p2 が空です。どちらか一方を指定してください。' ); } }); } /** * バリデーションエラーがある場合 JSON を返却する(動作確認用) */ protected function failedValidation(Validator $validator) { throw new HttpResponseException(new JsonResponse($validator->errors(), 422)); } }
FormRequest で withValidator メソッドを実装すると、親クラスが Validator を初期化した後に呼んでくれます。Validator はバリデーション処理をもろもろやってくれるやつです。after メソッドでコールバック関数を渡し、 Validator にフックをかけます。 バリデーション完了直後にこのコールバック関数を呼んでくれます。
結果は以下のようになります。
p2 なし | p2=1 | |
p1 なし | × | ○ |
p1=1 | ○ | × |
? {"p1":["p1 と p2 が空です。どちらか一方を指定してください。"]} ?p1=1 {"passes":true} ?p2=1 {"passes":true} ?p1=1&p2=1 {"p1":["p1 と p2 は両方指定できません。どちらか一方を指定してください。"]}
自作のバリデーションを他の Request でも再利用したい場合は extension として定義することも可能です。
まとめ
after hook を使えば、大抵のバリデーションは書けると思います。色々なフレームワーク触ってきましたが、 Laravel のバリデーションは簡単に書けるのがいいですね。