配列値の入力、つまり複数行に同じ入力項目を持つフォームの話です。お客様の登録などのフォームとは違って、バリデーションや画面でのエラーの表示など複雑な部分が多いです。しかし、コントローラで対応するより、FormRequestを使うとすっきりしたコードとなります。
配列値の入力フォーム
各行の入力項目が1つのケースから作成してみます。以下のような画面です。行数は固定で、最大3つまでのメールアドレスの入力が可能です。
コントローラのコードは、極力シンプルにして、投稿が成功したらバリデートした値を出力します。
namespace App\Http\Controllers; use App\Http\Requests\EmailsRequest; use Illuminate\Http\Request; class FormController extends Controller { /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create() { return view('form'); } /** * Store a newly created resource in storage. * * @param App\Http\Requests\EmailsRequest $request * @return \Illuminate\Http\Response */ public function store(EmailsRequest $request) { ddd($request->validated()); } }
ブレードは、
... <div class="container-fluid"> <div class="row"> <div class="col-md-12 col-lg-12 mt-2"> <div class="card card-primary"> <div class="card-header"> <h3 class="card-title">複数行のフォーム</h3> </div> <form method="POST" action="{{ route('form.store') }}" class="form-horizontal" novalidate=""> @csrf <div class="card-body"> @error('emails') <div class="invalid-feedback d-block">{{ $message }}</div> @enderror <div class="table-responsive"> <table class="table table-bordered table-hover table-sm"> <thead> <tr> <th>メールアドレス</th> </tr> </thead> <tbody> @for ($i = 0; $i < 3; $i++) <tr> <td> <input class="form-control" maxlength="255" name="emails[]" type="text" value="{{ old('emails.'.$i) }}"> @error('emails.'.$i) <span class="invalid-feedback d-block">{{ $message }}</span> @enderror </td> </tr> @endfor </tbody> </table> </div> </div> <div class="card-footer"> <button class="btn btn-primary float-right mr-2" type="submit">保存</button> </div> </form> </div><!-- card --> </div> </div><!-- row --> </div> ...
そして、FormRequestは、
namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class EmaisRequest extends FormRequest { /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'emails.*' => 'nullable|email|distinct', // nullableとしているのは空行を許すため ]; } public function messages() { return [ 'emails.*.email' => '不正なメールです', 'emails.*.distinct' => '重複の入力があります' ]; } }
実際に入力するとエラーはこんな感じで出力されます。
そしてバリデートされた値はこんな感じです。
しかし、これではDBに保存するときにいちいちnullを外す必要ありますね。これもFormRequestで処理したいです。
入力値の加工
FormRequestで入力値を加工するには、以下のようにprepareForValidation()
を追加して処理します。
... class EmailsRequest extends FormRequest { protected function prepareForValidation() { $emails = collect($this->emails) ->filter(function ($email) { return $email !== null; //ここでnull値を削除 })->all(); $this->merge([ 'emails' => $emails, ]); } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'emails' => 'required|array', //入力を必須とする 'emails.*' => 'nullable|email|distinct', ]; } public function messages() { return [ 'emails.required' => '必ず1つの入力が必要です', 'emails.*.email' => '不正なメールです', 'emails.*.distinct' => '重複の入力があります' ]; } }
最低1つの入力を条件とするために、'emails' => 'required|array',
もrules()
も追加しています。
さて、先の投稿でバリデートされたデータは、今度は以下のようにnullの値はなくなりました。
そして、何も入力がなく投稿された場合は以下のようなエラーとなります。
ちなみに、最低2つの入力を条件としたいときは、ルールを、'emails' => 'required|array|min:2',
とすればよいです。