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 のバリデーションは簡単に書けるのがいいですね。