フォームの中にたとえば「複数の商品を持つ」ような繰り返し領域があるケースは案外多いと思います。今回はそういった場合にどのようにバリデーションを設定するのか調べました。ちなみにバリデーションの実行の仕方はいくつかありますが、個人的にはLaravel5から追加されたFormRequestを積極的に利用してます。出てくるサンプルは全てFormRequestで設定しています。
まずHTML(blade)側はどのように記述するか
同じ名前で複数入ってくるものなのでinputのname属性は配列の形式で記述します。文字列[数字]
という形式です。
例)item_name[0]
対して、エラーメッセージが入っている$errorsへのアクセスは文字列.数字
という形式になります。
例)item_name.0
<div class="form-group @if($errors->has("item_name.{$idx}")) has-error @endif"> <div class="row"> <div class="col-sm-12"> <input type="text" class="form-control" name="item_name[$idx]" value="{{ old('item_name')[$idx] ?? '' }}" /> <div class="row has-error"> <div class="col-xs-12"> <span class="help-block">{{ $errors->first("item_name.{$idx}") }}</span> </div> </div> </div> </div> </div>
上記はbladeだけ想定して書きましたが動的にJavaScriptで差し込む場合もinputは同様のnameになるように中の数字を繰り上げてたとえばitem_name[2]
という形で追加します。(Mustache等のテンプレートエンジンを使うと簡単です)
バリデーションの設定
次にFormRequest側でどのように記述するのか書きます。あまり難しくなくて、$this->getで取得すると上記のようなHTMLで指定したものが配列で取得できるので、配列をループして例えばitem_name.{$key}
という形でルールを設定していけば良いです。
public function rules() { $itemRules = []; $keys = array_keys($this->get('item_name') ?? []); foreach ($keys as $key) { $itemRules["item_name.{$key}"] = 'required|max:100'; } return $itemRules; } public function attributes() { $itemAttributes = []; $keys = array_keys($this->get('item_name') ?? []); foreach ($keys as $key) { $itemAttributes["item_name.{$key}"] = "商品名[$key]"; } return $itemAttributes; }
attributesメソッドでも同様です。こちらは設定するとバリデーションのエラーメッセージの:attributeを任意の文字列に置き換える事ができます。
バリデーションの設定の仕方としてはこんな感じです。
FormRequestの利点
FormRequestでは上記のようにrulesを設定するのに使えるのですが、今見たようにフォームの構造に密接なのでこれを利用して、ぼくの場合はビジネスロジックに渡すパラメーターを生成するのに利用しています。
たとえばこんな感じです。
private function getItemsParams() { $items = []; $keys = array_keys($this->get('item_name') ?? []); foreach ($keys as $key) { $item = [ 'item_name' => $this->get('item_name')[$key] ?? '', ]; $items[] = $item; } return $items; }
こうしておくと、ControllerでFormのパラメーターをコネコネしなくてもRequestから取り出すだけなので簡潔ですしController側ではFormのパラメーターを知らなくても良いという利点もあります。