Laravel 5.4 での話。
FormRequest を用いている時、特定のフィールドの Validate Rule を、そのフィールド自身の値以外を用いて適用するかどうか等を決定したいときがある。
そんな時に、どうしたら都合が良いだろうか考えてみたメモ。
まず結論
laravel formrequest sometimes
とかで検索すると、 getValidatorInstance
メソッドを使う例が出てくるけど、 多分 withValidator
メソッドを使った方がいいよ。(主に test が書けるという理由で。)
Validator::sometimes
https://laravel.com/docs/5.4/validation#conditionally-adding-rules にあるように、何らかの条件を付与して、 Rule を適用するべきかどうかを判定することが出来る。
$v->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 100;
});
上記 URL に記載のある、この例だと、"games の値が 100 以上ならば、 reason を必須として、最大500文字以内とする"という条件となる。
games の値が 100 より小さい場合は、 reason は必須とならない。
Controller の中とかで、 Validator を用意して操作するぶんには、まぁ、これでも大丈夫。
FormRequest
https://laravel.com/docs/5.4/validation#form-request-validation のやつ。
Controller の Action で DI して使う便利なやつ。
rules
メソッドで必要な KEY と対応する rule を定義するだけで使える。
public function rules()
{
return [
'email' => [
'required',
'email',
],
];
}
と書いておくと、 email フィールドは必須かつ、 email 書式である、という条件で Validator が動いてくれる。
Controller の中とかで Validator を用意しなくて良いのでかなり見通しが良くなる。
それだけでも使う価値があると思う。(し、 unit test も少し楽になる。)
FormRequest + Validator::sometimes を使いたい
FormRequest を使って、先ほどの例の games の値によって reason に条件を付与したい場合どうしたら良いか。
ダメなやつ
public function rules()
{
return [
'reason' => [
'sometimes',
'required',
'max:500',
],
];
}
当然、これだと、 reason に値が存在する場合は必須かつ 500 文字以内となってしまう。
実現できるけどイマイチなやつ
https://stackoverflow.com/questions/30300119/how-to-use-sometimes-rule-in-laravel-5-request-class とか、 laravel formrequest sometimes
で検索すると出てくる。
FormRequest を extends した自前の Request Class で getValidatorInstance を Override し、その中で sometimes メソッドを使う。
例えば、 app/Http/Requests/Some/Path/StoreRequest.php
の中で、以下のように書く。
protected function getValidatorInstance()
{
$validator = parent::getValidatorInstance();
$validator->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 500;
});
return $validator;
}
これは期待する通りに動く。
ただし、 parent::getValidatorInstance()
してるので、 test を書く時に悲しい気持ちになる(はず)。
今のところ良さそうなやつ
FormRequest を extends した自前の Request Class で withValidator
メソッドを使う。
https://laravel.com/docs/5.4/validation の 「Adding After Hooks To Form Requests」に良さそうな事が書いてある。
This method receives the fully constructed validator, allowing you to call any of its methods before the validation rules are actually evaluated:
つまり、 rules を評価する前の状態の Validator を受け取れるので、お好きにどうぞってことだと思う。
after フックはわかり易い例として、他にも Validator への"追加的措置"が必要ならココに書くのが良いんやないかな。
例えば、 app/Http/Requests/Some/Path/StoreRequest.php
の中で、以下のように書く。
public function withValidator(\Illuminate\Contracts\Validation\Validator $validator)
{
$validator->sometimes('reason', 'required|max:500', function ($input) {
return $input->games >= 500;
});
}
こうすると parent::
も使わないので、 test しやすい!