3

My app has a form where I insert multiple records, each record is a new form. I want to validate each field in each form I tried to use validate function but I am confused how to do it for multiple records insert ? For this project I am using laravel 5.2.

Store Function For Multiple Insert

public function store(Request $request)
    {
        $this->validate($request,[
            'name' => 'required|min:4',
            'fname' => 'required',
            'rollno' => 'required|unique:students'
        ]);
        $input = $request->all();
        $condition = $input['name'];
        foreach ($condition as $key => $condition) {
            $student = new Student;
            $student->name = $input['name'][$key];
            $student->fname = $input['fname'][$key];
            $student->rollno = $input['rollno'][$key];
            $student->obtainedmarks = $input['obtainedmarks'][$key];
            $student->totalmarks = $input['totalmarks'][$key];
            $student->percentage = $input['percentage'][$key];
            $student->save();
        }
        return Redirect::to('/allresults');

}

View For Inserting data

@extends('layouts.app')
@section('content')
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<script type="text/javascript">
    $(function () {
        $('.add').click(function () {
            var n = ($('.resultbody tr').length - 0) + 1;
            var tr = '<tr><td class="no">' + n + '</td>' +
                    '<td><input type="text" class="name form-control" name="name[]" value="{{ old('name') }}"></td>'+
                    '<td><input type="text" class="fname form-control" name="fname[]" value="{{ old('fname') }}"></td>'+
                    '<td><input type="text" class="rollno form-control" name="rollno[]" value="{{ old('rollno') }}"></td>'+
                    '<td><input type="text" class="obtainedmarks form-control" name="obtainedmarks[]" value="{{ old('email') }}"></td>'+
                    '<td><input type="text" class="totalmarks form-control" name="totalmarks[]"></td>'+
                    '<td><input type="text" class="percentage form-control" name="percentage[]"></td>'+
                    '<td><input type="button" class="btn btn-danger delete" value="x"></td></tr>';
            $('.resultbody').append(tr);
        });

        $('.resultbody').delegate('.delete', 'click', function () {
            $(this).parent().parent().remove();
        });

        $('.resultbody').delegate('.obtainedmarks , .totalmarks', 'keyup', function () {
            var tr = $(this).parent().parent();
            var obtainedmarks = tr.find('.obtainedmarks').val() - 0;
            var totalmarks = tr.find('.totalmarks').val() - 0;

            var percentage = (obtainedmarks / totalmarks) * 100;
            tr.find('.percentage').val(percentage);
        });
    });
</script>

<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-default">
                <div class="panel-heading">Add Results</div>
                @if(count($errors) >0 )
                    <ul>
                        @foreach($errors->all() as $error)
                            <li>{{$error}}</li>
                        @endforeach
                    </ul>
                @endif
                <div class="panel-body">
                   <form class="form-horizontal" role="form" method="POST" action="{{ url('/result') }}">
                        {!! csrf_field() !!}
                    <table class="table table-striped">
                            <thead>
                                <tr>
                                    <th>ID</th>
                                    <th>Student Name</th>
                                    <th>Father Name</th>
                                    <th>Roll No</th>
                                    <th>Obtained Marks</th>
                                    <th>Total Marks</th>
                                    <th>%</th>
                                    <th>Delete</th>
                                </tr>
                            </thead>
                            <tbody class="resultbody">
                                <tr>
                                    <td class="no">1</td>
                                    <td>
                                        <input type="text" class="name form-control" name="name[]" value="{{ old('name') }}">
                                    </td>
                                    <td>
                                        <input type="text" class="fname form-control" name="fname[]" value="{{ old('fname') }}">
                                    </td>
                                    <td>
                                        <input type="text" class="rollno form-control" name="rollno[]" value="{{ old('rollno') }}">
                                    </td>
                                    <td>
                                        <input type="text" class="obtainedmarks form-control" name="obtainedmarks[]" value="{{ old('email') }}">
                                    </td>
                                    <td>
                                        <input type="text" class="totalmarks form-control" name="totalmarks[]">
                                    </td>
                                    <td>
                                        <input type="text" class="percentage form-control" name="percentage[]">
                                    </td>
                                    <td>
                                        <input type="button" class="btn btn-danger delete" value="x">
                                    </td>
                                </tr>

                            </tbody>
                        </table>    
                        <center><input type="button" class="btn btn-lg btn-primary add" value="Add New Item">   
                        <input type="submit" class="btn btn-lg btn-default" value="Submit"></center>
                        </form>
                </div>
            </div>
        </div>

    </div><!-- First Row End -->
</div> <!-- Container End -->

@endsection

Update : In the view you will notice there is a button when the user click on that "add new" it adds new row row I want to validate all the rows, each row contains a record. enter image description here

3
  • you can use wildcard validation Commented Jun 22, 2016 at 19:12
  • can you elaborate it @NahidBinAzhar Commented Jun 22, 2016 at 19:18
  • see the doc laravel.com/docs/5.2/validation#validating-arrays Commented Jun 23, 2016 at 7:46

5 Answers 5

7

I think you can do this to validate your array input.

'input_name.*' => 'rules'

https://laravel.com/docs/5.2/validation#validating-arrays

So in your case this:

$this->validate($request,[
     'name.*'   => 'required|min:4',
     'fname.*'  => 'required',
     'rollno.*' => 'required|unique:students'
 ]);
Sign up to request clarification or add additional context in comments.

9 Comments

It outputs ErrorException in helpers.php line 523: htmlentities() expects parameter 1 to be string, array given (View: D:\xampp\htdocs\ngresulty\resources\views\students\addresult.blade.php) error
That's because you are trying to get the 'old input'. But since it's an array you need to do it like this. {{ old('name.index') }}.
I tried to put 0 as index but still same error @z3r0ck
i even removed the value = old('name index ) thing but still the same error.
did you remove all the old inputs that is an array? I was able to reproduce the error and in my case it was the old('input') call that caused it.
|
4

Laravel 5.2 comes with bundle of ready features, for array validations you can take a look on Validating Arrays

In your case you should try :

   $validator = \Validator::make($request->all(), [
            'name.*' => 'yourRules',
            'fname.*' => 'yourRules',
            'rollno.*' => 'yourRules',
            'obtainedmarks.*' => 'yourRules',
            'totalmarks.*' => 'yourRules'
        ]);

  if($validator->fails()) {
            return back()->withInput()->withErrors($validator->errors());
  }

Another Solution:

The above solution is perfect and latest one, specially for Laravel 5.2 users. You can also try

$validator = \Validator::make($request->all(), [
    'name' => 'array',
    //and your other rules here
]);

After that use Validator each method to apply your defined rules to each element of the array.

$validator->each('name','yourRule');

1 Comment

I tried this before but the whole problem was that \ thing over there with Validator Class. Solved !
2

Form input arrays make things a little more complicated. You are making mistakes on various levels:

  • Not accounting for row index position.
  • Blade renders only one row when there may be more.
  • Blade syntax in javascript will never work.
  • Never calling your validator.
  • Saving valid records without considering that you may have to return the user if one of the records is invalid.
    • Choosing to return only invalid rows adds some more work but can be done.

Try the following as a start point. I didn't have time to test this all now but it follows logic I have successfully used many times.

view

@extends('layouts.app')
@section('content')
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>

<script type="text/javascript">
    $(function () {
        $('.add').click(function () {
            var n = $('.resultbody tr').length - 0;
            var n_adj = n + 1;
            var tr = '<tr><td class="no">' + n_adj + '</td>' +
                    '<td><input type="text" class="name form-control" name="name[n]" value=""></td>'+
                    '<td><input type="text" class="fname form-control" name="fname[n]" value=""></td>'+
                    '<td><input type="text" class="rollno form-control" name="rollno[n]" value=""></td>'+
                    '<td><input type="text" class="obtainedmarks form-control" name="obtainedmarks[n]" value=""></td>'+
                    '<td><input type="text" class="totalmarks form-control" name="totalmarks[n]"></td>'+
                    '<td><input type="text" class="percentage form-control" name="percentage[n]"></td>'+
                    '<td><input type="button" class="btn btn-danger delete" value="x"></td></tr>';
            $('.resultbody').append(tr);
        });

        $('.resultbody').delegate('.delete', 'click', function () {
            $(this).parent().parent().remove();
        });

        $('.resultbody').delegate('.obtainedmarks , .totalmarks', 'keyup', function () {
            var tr = $(this).parent().parent();
            var obtainedmarks = tr.find('.obtainedmarks').val() - 0;
            var totalmarks = tr.find('.totalmarks').val() - 0;

            var percentage = (obtainedmarks / totalmarks) * 100;
            tr.find('.percentage').val(percentage);
        });
    });
</script>

<div class="container">
    <div class="row">
        <div class="col-md-10 col-md-offset-1">
            <div class="panel panel-default">
                <div class="panel-heading">Add Results</div>
                @if(count($errors) >0 )
                    <ul>
                        @foreach($errors->all() as $error)
                            <li>{{$error}}</li>
                        @endforeach
                    </ul>
                @endif
                <div class="panel-body">
                   <form class="form-horizontal" role="form" method="POST" action="{{ url('/result') }}">
                        {!! csrf_field() !!}
                    <table class="table table-striped">
                            <thead>
                                <tr>
                                    <th>ID</th>
                                    <th>Student Name</th>
                                    <th>Father Name</th>
                                    <th>Roll No</th>
                                    <th>Obtained Marks</th>
                                    <th>Total Marks</th>
                                    <th>%</th>
                                    <th>Delete</th>
                                </tr>
                            </thead>
                            <tbody class="resultbody">

                                @for ($i = 0; $i < isset($row_count) ? $row_count : 1; $i++)
                                <tr>
                                    <td class="no">{{ $i+1 }}</td>
                                    <td>
                                        <input type="text" class="name form-control" name="name[]" value="{{ old('name['.$i.']', null) }}">
                                    </td>
                                    <td>
                                        <input type="text" class="fname form-control" name="fname[]" value="{{ old('fname['.$i.']', null) }}">
                                    </td>
                                    <td>
                                        <input type="text" class="rollno form-control" name="rollno[]" value="{{ old('rollno['.$i.']', null) }}">
                                    </td>
                                    <td>
                                        <input type="text" class="obtainedmarks form-control" name="obtainedmarks[]" value="{{ old('obtainedmarks['.$i.']', null) }}">
                                    </td>
                                    <td>
                                        <input type="text" class="totalmarks form-control" name="totalmarks[]" value="{{ old('totalmarks['.$i.']', null) }}">
                                    </td>
                                    <td>
                                        <input type="text" class="percentage form-control" name="percentage[]" value="{{ old('percentage['.$i.']', null) }}">
                                    </td>
                                    <td>
                                        <input type="button" class="btn btn-danger delete" value="x">
                                    </td>
                                </tr>
                                @endfor

                            </tbody>
                        </table>    
                        <center><input type="button" class="btn btn-lg btn-primary add" value="Add New Item">   
                        <input type="submit" class="btn btn-lg btn-default" value="Submit"></center>
                        </form>
                </div>
            </div>
        </div>

    </div><!-- First Row End -->
</div> <!-- Container End -->

@endsection

store()

use Validator;

// ...

public function store(Request $request)
    {

        $validation_rules = [
            'name' => 'required|min:4',
            'fname' => 'required',
            'rollno' => 'required|unique:students'
        ];

        $validation_messages = [
            // add custom error messages
        ];

        $name = $request->input('name');
        $fname = $request->input('fname');
        $rollno = $request->input('rollno');
        $obtainedmarks = $request->input('obtainedmarks');
        $totalmarks = $request->input('totalmarks');
        $percentage = $request->input('percentage');

        // number of rows submitted (use a required value)
        $n = count($names);

        // track valid student records
        $students = [];

        for ($i=0; $i < $n; $i++) {
            $data = [
                'name' => $name[$i],
                'fname' => $fname[$i],
                'rollno' => $rollno[$i],
                'obtainedmarks' => $obtainedmarks[$i],
                'totalmarks' => $totalmarks[$i],
                'percentage' => $percentage[$i]
            ];
            $validator = Validator::make($data, $validation_rules, $validation_messages);

            if ($validator->fails()) {
                // redirect back to form
                return redirect()->back()->withInput($request->all())->with('errors', '$validator->errors());
            }

            $students[] = $data;
        }

        foreach ($students as $row) {
            Student::create($row);
        }

        return redirect('/allresults');

}

4 Comments

Not accounting for row index position. I completely disagree with that statement. Look at this: adamwathan.me/2016/04/06/cleaning-up-form-input-with-transpose
@ÁlvaroGuimarães Transpose looks interesting... What part of the statement do you disagree with? Look at the question asked and please point out where the code is either using Transpose or is specifying the row index as needed for a dynamic amount of rows.
My comment looked rude, it was not my intention, sorry. The statement I disagree is: "Not accounting for row index position". Your answer is correct, but I don't think you NEED to control the row index position.
@ÁlvaroGuimarães I appreciate your consideration but assure you that your comment was not read by me as rude. Once I reached the code I have shared I stopped testing so I can't say that it won't work without a specified row index position, it just seems that blade has to create one row at a time and I'm not sure how old() would know which row was relevant in the current loop.
0
$failed = false;
$errors = [];
        foreach($data['name'] as $key => $val){

            $v = Validator::make($val, [
                        'name' => 'unique:users'
             ]);



             if ($validator->fails()) {
                $failed = true;
                 $error = [
                     'msg' => $v->errors()->all(),
                     'number' => $val['name'],
                 ];
                 array_push($errors, $error); 
             }

        }

if($failed == true){
    return['msg' => false, 'data' => $errors];
}

Try this one :)

1 Comment

Create multiple Validator instance isn't the best solution, Laravel can validate arrays
0

For Multi record validation use like this with custom message with record number

<?php

namespace App\Http\Requests\Modules\Export\ExportBooking;

use App\Rules\PhoneRule;
use App\Traits\ApiResponser;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Contracts\Validation\Validator;
use Illuminate\Http\Exceptions\HttpResponseException;


class ExportBookingCreateRequest extends FormRequest
{
    use ApiResponser;

    public function rules()
    {
        $rules = [
            'booking_date' => 'required|date',
            'exp_organization_id' => ['required','integer', Rule::exists('organization', 'id')->where('is_export', 1) ],
            'marketing_person_id' => 'required|integer|exists:marketing_person,id',
            'customer_id' => 'required|integer|exists:customer,id',

            'exp_booking_dtl' => 'required',
        ];

        $exp_booking_dtl = $this->input('exp_booking_dtl');

        foreach ($exp_booking_dtl as $rowNumber => $item) {
            $rules["exp_booking_dtl.{$rowNumber}.product_id"] = "required|integer|exists:product,id";
            $rules["exp_booking_dtl.{$rowNumber}.product_sticker_id"] = "required|integer|exists:product_sticker,id";
            $rules["exp_booking_dtl.{$rowNumber}.po_number_id"] = "nullable|integer|exists:po_number,id";
            $rules["exp_booking_dtl.{$rowNumber}.qty"] = "required|numeric|gt:0";
        }
        return $rules;
    }
    public function messages()
    {
        $messages = [];
        $exp_booking_dtl = $this->input('exp_booking_dtl');

        foreach ($exp_booking_dtl as $rowNumber => $item) {

            $messages["exp_booking_dtl.{$rowNumber}.product_id.required"] = "Booking Detail Rec-".($rowNumber+1).": Product is required.";
            $messages["exp_booking_dtl.{$rowNumber}.product_id.exists"] = "Booking Detail Rec-".($rowNumber+1).": Invalid Product.";

            $messages["exp_booking_dtl.{$rowNumber}.product_sticker_id.required"] = "Booking Detail Rec-".($rowNumber+1).": Product Sticker is required.";
            $messages["exp_booking_dtl.{$rowNumber}.product_sticker_id.exists"] = "Booking Detail Rec-".($rowNumber+1).": Invalid Product Sticker.";

            $messages["exp_booking_dtl.{$rowNumber}.po_number_id.exists"] = "Booking Detail Rec-".($rowNumber+1).": Invalid PO Number.";

            $messages["exp_booking_dtl.{$rowNumber}.qty.required"] = "Booking Detail Rec-".($rowNumber+1).": Qty is required.";
            $messages["exp_booking_dtl.{$rowNumber}.qty.gt"] = "Booking Detail Rec-".($rowNumber+1).": Qty must be greater than :value.";
        }
        return $messages;
    }
    public function failedValidation(Validator $validator)
    {
        throw new HttpResponseException(
            $this->set_response(null, 422, 'error', $validator->errors()->all(), new Request($this->request->all()))
        );
    }
}


Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.