4 min read

How To: Validate an array of form fields with Laravel

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:

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.