How To: Validate an array of form fields with Laravel

Note: This tutorial is only relevant for Laravel 5.1 and lower. Laravel 5.2 has improved how this works.

If you’ve used Laravel’s form validation for any length of time, then you know it’s a powerful system. It makes the tedious task of validation very simple while still keeping the door open for complex rules.

In this tutorial, I want to show you a simple and easy way of validating forms that contain dynamic fields. A common use case for these types of forms is when you would like to allow a user to add more fields to a form.

Here is a visual representation of the form:

Order Form

In a real world scenario clicking the “Add New” button would load in a new input field with the field name being an array. All of that would happen with JavaScript and is beyond the scope of this tutorial however if you’d like to see a quick way of doing it have a look at this thread.

Here is a quick PHP mockup of the view for demonstrating the form fields:

<input type="text" class="form-control" name="name" value="name">

@for ($i=0; $i < 2; $i++)
    <input type="text" name="items[{{ $i }}]" value="{{ $i }}">
@endforeach

To handle the actual validation Laravel gives us a few options. The Validation::make facade, Controller validation, and the new Form Requests. For this tutorial, I’ll step through using the new Form Request feature. This same code can be adapted to both the other options.

Creating a new form request

To create the Form Request class, we can utilize Artisan to have the system automatically generate the file. Open terminal and cd into your directory and then run:

$ php artisan make:request OrderRequest
Request created successfully.

Next open this new file which is at the following location app/Http/Requests/OrderRequest.php. You should see this content:

<?php namespace AppHttpRequests;

use AppHttpRequestsRequest;

class OrderRequest extends Request {

  public function authorize()
  {
    return false;
  }

  public function rules()
  {
    return [
      //
    ];
  }
}

Adjust the Authorize

The first step is to modify the authorize() method. This method allows you to restrict access by returning true or false. For example, you could check a user group, or some other form of access and deny it if they failed to be a part of the group. Change this to return true to bypass any checks:

public function authorize()
{
  return true;
}

Adding Custom Validation Rules

In that same file, you’ll see the next method is rules and this where you need to define you custom validation rules. You can find a complete list of rules via the official documentation. In our case, we want to keep it simple and force the name field to be required, and then each book title the user would like to purchase must be less than 10 characters in length.

You can use any logic you’d like in the rules() method as long as you return an array.

In order to handle the dynamic fields, you will need to loop through all the posted “items” and add a rule for each. Here is an updated method demonstrating this:

public function rules()
{
  $rules = [
    'name' => 'required|max:255',
  ];

  foreach($this->request->get('items') as $key => $val)
  {
    $rules['items.'.$key] = 'required|max:10';
  }

  return $rules;
}

What we are doing here is first setting the required name field and then looping through all of the $_POST items to dynamically set the validation for each one. The important area to take notice of is the array key. Laravel allows you to use the dotted syntax for each item. For example $rules[‘items.1’] would map to:

<input name="items[1]"

This can be confusing if you are new to the framework and is easy to miss.

If you now try to submit the form in the browser, you may notice a problem. The failed validation gives us this error message:

laravel-failed-validation

Your user at this point is going to be scratching their head thinking what is “items.0”? To fix this Laravel also allows a messages method where you can set any custom messages that will be returned to the user.

Form Request Validation Messages

To fix this, you will need to add a custom message for each item. Here is the completed method:

public function messages()
{
  $messages = [];
  foreach($this->request->get('items') as $key => $val)
  {
    $messages['items.'.$key.'.max'] = 'The field labeled "Book Title '.$key.'" must be less than :max characters.';
  }
  return $messages;
}

Just like rules() we are looping through the items and specifically targeting the max rule to display a custom message and more importantly remove the “items.0”. Now our error message will be displayed like this:

Laravel Custom Validation Rules

Conclusion

As you can see, Laravel’s form validation is powerful, and the new form requests are a nice addition to Laravel 5. If you’d like to see all the code associated with this tutorial check out the complementary GitHub repo. If you have any questions or comments feel free to share below.

0 Replies to “How To: Validate an array of form fields with Laravel”

  1. How would you deal with array validation with multiple fields grouped together? For example you now want the user to enter their favourite character in each of their favourite books?

    1. It would pretty much be the same. Loop through the request and add each character into the $rules array. The important part would be using the dotted syntax in the array key:

      $rules[‘items.’ . $key . ‘.’ . $character]

    1. Ideally I’d have JavaScript validation first which should catch 95% of any errors. Going off the top of my head I’d think you would want to check the Input for “items” then loop Input::get('items') in your view and have the fields display again with the value set.

      Sort of hard to explain in a comment, but I think that would be the basics of it. If you’d like me to clarify more or give an example let me know.

      1. Hey! Related to dynamically created fields, I’ve run into a problem. I’ve tried sending the inputs to view – at validation failed, check if the array it is set, is yes, run a foreach to recreate the inputs and populate them – but the inputs are not displayed and so the values are missing). It would be amazing if you could show me a simple example. By the way, I’m on laravel 4. Thank you

    1. When you say “file input” are you attempting to upload files and validating those? Maybe you can create a gist with your FormRequest code so I’d get a better idea of what you are doing.

      1. Just resolved my problem. To get file inputs into “FormRequest” class I needed to use this code:

        foreach($this->files->get('club_logo') as $key => $val) {
            $rules['club_logo.'.$key] = 'max:5000|mimes:png,jpg,jpeg';
        }
        
  2. Hi, nice post. But how do you handle the rules if every text fields are populated base on the result of database fetch? The field name would be the id of database result

  3. How do manage to put each error near each field? $error->first(‘items.0’) works but what happens if the items are added dynamically?

  4. I’m using your approach to validate input arrays, but Laravel doesn’t seem to remove the index while testing for the “unique:tablename” rule, so for example if I have $rules[‘username.’.$idx] = ‘required|max:60|unique:users’; Laravel tries to find a column called “esername.8” (for example) in the users table. Or maybe I’mmissing something? 😀

    1. That is a good question and one I haven’t tested. However, the docs say:

      The field under validation must be unique on a given database table. If the column option is not specified, the field name will be used.

      So it seems like you need to just define this:

      $rules[‘username.’.$idx] = ‘required|max:60|unique:users,username';
      
  5. The “Book Titled 0” stills seems a little bit vague to the user, will it not be better to insert the value of the index?e.g the “Book Titled Merchant of Venice,,,,,

  6. Hi, good tutorial but I’m stuck on the extra part where only 1 of 4 for fields is required. And I can’t seem to find a way to use the array of form fields with the “required_without_all”.

    I currently have this:
    [code]’clientNumber’ =&gt; ‘required_without_all:friend_clientNumber.0, friend_clientNumber.1, friend_clientNumber.2|clientnumber’,[/code]

    As you can see only one of these client number fields is required. But the . notation doesn’t seem to work here..?

    Do you have any suggestion for that?

    1. Not the most pretty, but I guess this works:

      public function rules()
      {
      $products = [];
      $rules = [
      'name' => 'required|max:255',
      ];

      foreach($this->request->get('product') as $key => $val)
      {

      $notin = implode(',',$products);
      if( $notin != ''):
      $rules['product.'.$key] = 'not_in:'.$notin;
      endif;
      $products[] = $val;
      }

      return $rules;
      }

  7. “namespace AppHttpRequests;” ???? It’s a namespace ?

    “use AppHttpRequestsRequest;” ???? and use this namespace ?
    It’s a better this :
    <?php namespace AppHttpRequests;
    use IlluminateHttpRequest;

  8. Hi, thank you for the information but it’s not working.

    For example, if the user did not submit any items, the validation rules won’t even kick up. I need to validate so user need to submit at least one item.

    I’m migrating my site from Codeigniter to Laravel, in Codeigniter it’s much more easier. You just put ‘[]’ behind the field name, so just use ‘items[]’ as the field name and the validation will work on array.
    The array file validation also has bug too, it won’t return error message.

    I wonder if my decision to move was wrong..

    1. I just found out that Laravel doesn’t support composite primary key too.. I can do this with Codeigniter, I really regretted migrating to Laravel now that it’s almost finished 🙁

  9. Hi Eric, Nice Tutorial
    I am using Laravel 5.2 but haven’t found a way to fill up a select in a form array with the proper old() values even when I am passing the correct old(). form helper is setting the selected to all the inputs in the array which were selected . Has anybody found a way for this this.
    Thanks.

  10. I’m trying to validate this:

    {!! Form::text(‘text[]’, null, array(‘class’ => ‘form-control’)) !!}

    {!! Form::number(‘points[]’, null, array(‘class’ => ‘form-control’)) !!}

    So I’ve followed your post and I’m using:
    public function rules()
    {
    $rules = [
    // some stuff
    ];

    // validate dynamic choices
    foreach ($this->request->get(‘text’) as $key => $val) {
    if (!empty($val)) {
    $rules[‘text.’ . $key] = ‘required’;
    $rules[‘points.’ . $key] = ‘required|integer’;
    }
    }
    return $rules;
    }

    But when I try to submit a `points` field without value It says this error:

    htmlentities() expects parameter 1 to be string, array given

    1. @Paco Orozco
      I also got this type error but i change html in your code change into html like text[] to text[0],text[1]…
      and point[] to point[1],point[2].. may be your code will working .

  11. public function rules()
    {
    // print_r($this->request->get(‘name’));die;

    $rules = [
    ];
    foreach($this->request->get(‘name’) as $key => $val)
    {
    $rules[‘name.’.$key] = ‘required’;
    }

    foreach($this->request->get(‘quantity’) as $key => $val)
    {
    $rules[‘quantity.’.$key] = ‘required’;
    }

    foreach($this->request->get(‘totalprice’) as $key => $val)
    {
    $rules[‘totalprice.’.$key] = ‘required’;
    }
    return $rules;
    }

    I have done ass you said but still same problem as :

    ErrorException in helpers.php line 469:
    htmlentities() expects parameter 1 to be string, array given (View: localresourcesviewsgoods_increate.blade.php)

Leave a Reply