Exploring validation with Laravel 5

Posted in: PHP

Dec 5, 2015

One of the most critical skills any developer must have is their ability to validate user input. Leaving users to just enter any old information could lead to some catastrophic results, even if the use does it unintentionally. 

While HTML has advanced to a stage where it offers its own native validation, client-side validation is still easily circumnavigated. This means that server-side validation should never just be an optional nice-to-have but rather a requirement for any project. With Laravel 5, validation is extremely simple due to its robust code base. There are several ways of validating in Laravel 5 and we'll consider each of them below. 

Using the validate() method

For any new controller you create in Laravel, you'll always be having to extend the base Controller class. What you may not be aware of is that by extending the base class you'll actually be given access to something called the ValidatesRequest trait. This trait provides you with a bunch of useful features, most notably the validate method. 

I've set up a new controller via Larave's Artisan CLI, which will automatically extend the aforementioned base Controller class as well as provide some basic scaffolding such as index, create and store methods.

php artisan make:controller PostController

Since the store method is what is usually invoked on POST requests, that is where I'll be demonstrating the validation techniques in this article. If you're unsure of what I mean or are unsure on how to test this yourself by invoking the store method, then you'll first have to set up your routes.php file, ideally with a resource route, and quickly throw together some views information on all this is found of Laravel's official documentation site.

Since the controller is using the ValidatesRequest trait, you can call the validate method off the controller itself by the way of $this. The method requires two arguments. First is a Illuminate\Http\Request instance since that is what will hold your input data. Because we're using the store method we have an instance available to us immediately within the $request variable. The second argument that must be supplied is the actual validation rules you want to test against and this should be passed as an associated array. A typical invocation will look like this:

public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required|unique:posts|max:255',
        'content' => 'required'
    ]);
}

The associative array's property names should match the property names that would usually be in the $_POST array or the $request->all() array, that is, the name attributes of your input fields. The property values are the rules that should be applied to the fields. A value can contain many rules, and so if you do intend to include more than one, you must separate it with a pipe | character. 

If this doesn't sound immediately clear, then simply look at the example provided above. I'm validating two fields, a title field and a content field, so 'title' and 'content' are the properties on my array. The title property is getting tested against three validation rules, the required rule, the unique rule and the max rule, each is separated with a pipe |. The content property is only getting tested against the required rule. 

The rules I'm using are built into Laravel itself, and just by reading them it should be pretty clear on what they each do. The required rule simply tests to see if the field is present and that its value isn't an empty string or falsey equivalent. The max rule tests against a maximum number of characters. The unique rule tests if the given property is unique against a database table column that shares that same name. Which table the column belongs to is indicated by the colon : and is this case I'm testing to make sure the title doesn't exist in the title column on the posts table. 

Those rules are just three of many, and to see the much larger variety of rules you have at your disposal, check out the Laravel docs here

If your input passes all the provided validation rules, the program will continue to move through the store function, otherwise Laravel will automatically send a redirect response, and flash a Illuminate\Support\MessageBag instance to the session to provide error messages to the client. However if this were an AJAX request, than instead of a redirect response, a JSON response will be generated with a 422 header. 

We'll examine error handling later in this article. 

Manually creating a validator instance

The second approach to validation is by manually creating a Validator instance and is a method that may be more familiar to you if you're coming from Laravel 4. 

To utilise this approach you must first include the Validator Facade in your controller:

//PostController.php
namespace App\Http\Controllers;

use Validator;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;

class PostController extends Controller
{
    //
}

With the Validator facade included, you can then create an instance by invoking its ::make() static function. ::make(), like the validate() method, requires two arguments, the input to be validated and the validation rules. 

Whilst the validate() method expected an instance of Illuminate\Http\Request for its first argument, the ::make() method will expect an associative array for input data. The rules however, are passed in the same way as an associative array. 

$validator = Validator::make($request->all(), [
                'title' => 'required|unique:posts|max:255',
                'content' => 'required'
            ]);

In the above example, you can see that I was able to use the $request instance's all() method as my first argument since it returns an associative array, however you can manually create and pass any sort of associated array to validate against. 

The above code also stores the Validator instance in the $validator variable, which we can use to check for success or not. Again this is different from the validate() method since that automatically checked for success / failure and returned a redirect response. 

The method we will need to next invoke is the fails() method which returns a boolean value based on whether or not the first argument passed into Validator::make() passed the validation rules of the second argument.

if($validator->fails())
{
    return redirect('post/create')
        ->withErrors($validator)
        ->withInput();
}

In the code above we can see that by use of the fails() method we can test the validation in a conditional block. If it does fail (fails() evaluates to true) then we must instruct the program what to do next, and that usually is to build a redirect response. As I'm using a route resource the redirect path is the standard '{model}/create'. With your redirect path specified it is recommended to include the withErrors method and pass in the $validator variable to flash error messages to the session. Finally it is also recommended to include withInput() so that form fields are re-populated with old user data. As you can see, if you want more control on how your program responds to failed validation, then the manual Validator approach does have its advantages. 

Form request validation

The third and final validation method we'll be examining is validation via the creation of what is known as a 'form request'. This is my personal preferred way as it allows for clear separation of my validation and controller logic. Form request validation works by validating your input before executing code in your controller methods. To see what I mean, we first have to create a custom request class, which is most easily achieved by the artisan CLI.

php artisan make:request PostRequest

Artisan will generate the file which will live in http/requests/. The file itself will come with some scaffolding; it'll extend the Request class and come with an authorize method and the rules method. 

Before we examine the our custom request any further, we first need to tell Laravel to use it. Back in the PostController I've replaced the generic $request object in the Store function with a PostRequest instance. 

//PostController.php
public function store(PostRequest $request)
{
    //
}

Now when you call the store method as the result of a form submission, the program will first execute your PostRequest before even moving to any of the code in the store method. Since PostRequest will handle all validation, so now only validated input will reach your controller and anything else will be redirected with a response. 

So back in the PostRequest class, let's examine first the authorize() method. This method returns a boolean, it its true then the program will next invoke the rules() method. If false, the user will receive a 403 forbidden response. The authorize() method is perfect for determining whether the user who made the request is authorized, so perhaps they need to have a certain user id, or have the correct privileges if you're using access control lists. Generally I might just check to see if a user is logged in (note that I've included the Auth facade in this example):

public function authorize()
{
    return Auth::check();
}

The next method on the form request to examine is the rules() method. In this method you must return an associative array of your fields and their validation rules, similar to what you would pass in as the second argument in either the validate() method or Validate::make().

public function rules()
{
    return [
        'title' => 'required|unique:posts|max:255',
        'content' => 'required'
    ];
}

Working with error messages

Earlier we saw that in the case of a failed validation, a redirect response was generated and with errors being passed. Using the validate() method and form request validation techniques, Laravel will do this automatically for us, whereas with a manually created validation instance we do this ourselves. No matter how the errors will be passed to our views, we must set to display them ourselves. 

The errors as stated earlier are flashed to the session. The are accessible via an $errors variable which is an instance of Laravel's Illuminate\Support\MessageBag. Before we look at how to actually make use of the MessageBag instance, there is one more thing that is useful to know about the $errors variable and this is that it is always available on every request. What this means is that on our views we can safely write something like this:

@if(count($errors) > 0)
    //
@endif

So even when $errors is not actually assigned with a MessageBag instance (such as during a GET request), it is still available in a view, and code such as the above will not throw an undefined variable error. 

Building upon the above example, you are also able to iterate through all the available error messages by calling the all() method within a foreach loop:

@if(count($errors) > 0)
    <ul>
    @foreach($errors->all() as $error)
        <li>{{ $error }}</li>
    @endforeach
    </ul>
@endif

If you can imagine this code in a view, this would predictably render a

  • element with a list of error messages. This is useful, but perhaps you would like to more directly access each error message and display it next to its associated input field. To do that, we can call the first() method and pass in, as an argument, the name attribute of the field (which should be what the validator used as well):
<input type="text" name="title" />
{{ $errors->first('title') }}

The first() method will retrieve the first error encountered for that field that didn't pass. The above example does introduce a couple of problems however, such as, what if the field has more than one error? If you recall in our validation examples earlier, the title field had a required rule, a unique rule and a max character rule. The other problem is that this code doesn't consider if the errors even exist at all. To address both of these concerns we can use the has() method and then apply a foreach loop which invokes the get() method:

<input type="text" name="title" />
@if($errors->has('title'))
    <ul>
        @foreach($errors->get('title') as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif

Before we move on from the $errors variable, there's one last thing I quickly want to mention and that is that you can also set a custom format. The get(), all() and first() methods all specify an optional format parameter which allows you to provide some basic html in an argument that will tell Laravel how to render your error. Simple pass in some html as a string as your second argument, with the special keyword :message that will be used as a placeholder for the message itself:

<input type="text" name="title" />
@if($errors->has('title'))
    <ul>
        @foreach($errors->get('title', '<li>:message</li>') as $error)
            {{ $error }}
        @endforeach
    </ul>
@endif

Custom error messages

If you've been trying these techniques as you've been reading, you might have seen by this point that the actual messages that are printed might not be descriptive enough for you. Fortunately Laravel offers a simple way of allowing you to create your own custom error messages for each of your validation rules. If you have set up your validation using the validate() method or have manually created a Validator instance, then you can just pass in an optional third argument to when you call either validate() or Validator::make(). The argument passed should be an associative array where the property names are the field which you are validation appended with a period . and a validation rule. The values for these properties should be the messages. For the validate method, it would look like:

public function store(Request $request)
{
    $this->validate($request, [
        'title' => 'required|unique:posts|max:255',
        'content' => 'required'
    ],
    [
        'title.required' => 'The title field is required',
        'title.unique' => 'Sorry sparky, but a post already exists with that title!',
        'title.max' => 'You really gotta be less verbose with your titles!',
        'content.required' => 'Your post, sorta, like, needs some content'
    ]
);
}

The Validate::make() approach is essentially the same:

public function store(Request $request)
{
    $validator = Validate::make($request->all(), [
        'title' => 'required|unique:posts|max:255',
        'content' => 'required'
    ],
    [
        'title.required' => 'The title field is required',
        'title.unique' => 'Sorry sparky, but a post already exists with that title!',
        'title.max' => 'You really gotta be less verbose with your titles!',
        'content.required' => 'Your post, sorta, like, needs some content'
    ]
);
}

So this begs the question, how do we provide custom messages on a form request class? Thankfully although the approach is slightly different it is just as simple. Simply create a public function messages, which returns an associative array in the exact same format as the ones we just examined:

//PostRequest.php

public function messages()
{
    return [
        'title.required' => 'The title field is required',
        'title.unique' => 'Sorry sparky, but a post already exists with that title!',
        'title.max' => 'You really gotta be less verbose with your titles!',
        'content.required' => 'Your post, sorta, like, needs some content'
    ];
}

So hopefully you can see that validation in Laravel can be achieved in a number of ways, but no matter which approach you use, they are all very easy to implement. Of course, like most things in Laravel, validation can be extended and has loads of functionality even in its core. In this article I've barely scratched the surface on Laravel's validation libraries, but what I have covered should serve you for most cases you need. Thanks for reading, and as always, happy coding!