前置き
laravelのバリデーションでは条件を簡便に書くことができる
これ自体は便利なのだが、複雑なことをやろうとすると若干物足りないことがある。
やりたいこと
0と1の値を持つラジオボタンが2項目ぶん、項目AとBとして用意されており、その直下にテキストエリアが項目Cとして用意されている。
<input name="radioa" value="0" type="radio" checked>
<input name="radioa" value="1" type="radio">
<input name="radiob" value="0" type="radio" checked>
<input name="radiob" value="1" type="radio">
<textarea name="textc"></textarea>
このラジオボタンの両方が1の場合のみ、テキストエリアが空欄かどうかのチェックをしたい。
required_ifを使う(失敗)
このようなとき、laravelのバリデーションの機能を使おうとすると大抵はrequired_ifが思い浮かぶ。
片方のラジオボタンだけ見るのなら、radioaが1のときtextcの内容を確認したいので、
'textc' => 'required_if:radioa,1',
とすればいいが、
これに加えてradiobも見たいとなると
'textc' => 'required_if:radioa,1|required_if:radiob,1|emoji',
これではradioaかradiobのどちらかが1のときにrequiredのチェックが走ってしまいうまくいかない、つまりOR条件になってしまう。
条件をつなぐパイプ(|)を&に変えたらどうだろうかなどと考えたがだめだった。(それはそう)
クロージャで対処(成功)
いろいろ調べたが、required_ifだけではandのような挙動をもたせることはできないようなので、おとなしくクロージャを使うことにした
'textc' => [
// required_ifではORになり要件を満たせないのでクロージャを使う
function ($attribute, $value, $fail) use ($request) {
// 入力データの取得
$input_data = $request->all();
// radioaとradiobがどちらも1の場合
if ($input_data['radioa'] === '1' && $input_data['radiob'] === '1') {
// textcが空欄か
if (empty($input_data['textc'])) {
$fail('エラーメッセージ');
}
}
},
// 上記以外にも条件があれば続けて書く
'max:1000',
],
FromRequestを継承している場合はuse以降は不要で、$this->all()
で読み出せる。
こういう特殊な条件はだいたいそのフォーム固有のことが多いのでクロージャでの対応でもまぁ問題ないと思われ。
もしあちこちで使うならばRulesに定義して読み出すほうが簡潔で綺麗。