入力画面のバリデーションでちょいと頭をひねったケースです。2つの入力項目があり、どちらかが必須なのだけれど両方の入力は禁止という場合です。まず、具体的な例を見てみましょう。
簡単化しましたが、要はお客さんに配るクーポンのレコードの作成です。
注目してもらいたのは「割引」の項目で、2つの入力場所があります。ひとつは割引額で円で入力、もうひとつは割引率で%で入力です。
コントローラのコードはこんな感じです。新規のDBレコード作成の部分だけですけれど。
namespace App\Http\Controllers; use Illuminate\Http\Request; class CouponController extends Controller { /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { return view('coupon.create'); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $rules = [ 'code' => 'required|alpha_numeric', 'discount_amount' => 'nullable|required_without:discount_rate|integer|gt:0', 'discount_rate' => 'nullable|required_without:discount_amount|integer|gt:0|lte:100', ]; $validated = $request->validate($rules); // DBに保存のコードがここに入る } ...
discount_amount(割引額)と discount_rate(割引率)のバリデーションに注目してください。nullableは両方とも空になるかもしれないゆえに必要です。
次の、required_withoutは指定する項目が空のとき(つまりnull)はバリデーションの対象の項目が必須となる、ということです。両方の項目でお互いの項目を指定しているのは、両方が空となっては困るからです。つまり、どちらかの入力が必須となります。
よさそうですね。
と思いきや、この問題は両方に値を入れてもパスしてしまうことです。
その問題に対応するために思いついたのは、以下のようにバリデーションに匿名の関数を使用します。
... $rules = [ 'code' => 'required|alpha_num', 'discount_amount' => [ 'nullable', 'required_without:discount_rate', function ($attribute, $value, $fail) { if (request('discount_rate')) { $fail("割引額あるいは割引率どちらかだけ入力してください"); } }, 'integer', 'gt:0', ], 'discount_rate' => 'nullable|required_without:discount_amount|integer|gt:0|lte:100', ]; ...
request()のヘルパーがここで役に立ちます。バリデーション対象以外の項目の値を簡単に取得できます。
最後に、今回の「どちらかに入力してください」のケースはそう起こることではないと思いますが、UIを変えてクリエイティブな対応も可能と思います。例えば、以下のように割引額と割引率をラジオボタンとすると、それぞれに対応する値の入力項目は2つでなく1つになり常に必須とすることできます。もちろん、それはそれでバリデーションのルールが簡単になるかどうかは保証なしですけれど。
メルマガ購読の申し込みはこちらから。