Laravel 5 New Awesome Form Validation

Laravel 5 New Awesome Form Validation
Laravel 5 Form Validation Screenshot

The upcoming Laravel release i.e, 5 will be introducing some major changes in the Laravel Framework. The notable changes will be its new directory structure, the use of psr-4 auto-loading standard by default, support for Dependency Injection on Route and Controller methods and the one we will be discussing in this article i.e, form validation and form requests.

Form Validation Using FormRequest Class

The new Illuminate\Foundation\Http\FormRequest class will be serving as the base for the form request validation. By using the Request objects extended from Illuminate\Foundation\Http\FormRequest class in conjunction with the method injection, we will be able to write pretty thin controllers free of validation jargon. Consider the following example code for a UsersController which is responsible for registering the users:

<?php namespace App\Http\Controllers;
//file: app/Http/Controllers/UsersController.php

use Response;
use Illuminate\Routing\Controller;
use App\Http\Requests\UserFormRequest;

class UsersController extends Controller {

    public function getRegister()
    {
        return view('master')->nest('content','register');
    }

    public function postRegister(UserFormRequest $request)
    {
        //$user = new User;
        //$user->username = $request->username;
        //...
        //...
        //$user->save();
        return Response::make('Registration successful!');
    }
    //other controller methods...
}

For the Curious
Laravel 5, internally binds an event listener to the the router.matched event, the listener watches for the instances of the FormRequest class and calls the validate method automatically whenever it finds one ( see Illuminate\Foundation\Providers\FormRequestServiceProvider.php ).

Now, in the above code when the route for the postRegister action will be matched, the validate method on the UserFormRequest will be automatically called. In the case of a successful validation and passed 'authorization' checks the control will be transfered to the postRegister action. Now let us see the code for the UserFormRequest:

<?php namespace App\Http\Requests;
//file: app/Http/Requests/UserFormRequest.php

use Response;
use Illuminate\Foundation\Http\FormRequest;

class UserFormRequest extends FormRequest {

    public function rules()
    {
        return [
            'username' => 'required|alpha_dash',
            'email'    => 'required|email',
            'password' => 'required|between:8,20|confirmed',
            'age'      => 'required|integer|max:60'
        ];
    }

    public function authorize()
    {
        return true;
    }

}

In case of a failed validation, you will be redirected to the previous URL by default. This behavior can be changed by defining the protected $redirect property in the child class.

In the above code, both of the methods are required to be implemented for the request validation to work properly. A quick glance at the code of the FormRequest class reveals that after validating the input, the FormRequest class tries to call the authorize method and if it does not find one, returns a Forbidden 403 response. So, if an action does not require authorization, you should return a true value from authorize method to reach the called controller action.

The authorize method is going to make things a lot easier in the future from the authorization perspective. Consider the following naive example of UserFormRequest class:

<?php namespace App\Http\Requests;
//file: app/Http/Requests/UserFormRequest.php

use Response;
use Illuminate\Foundation\Http\FormRequest;

class UserFormRequest extends FormRequest {

    public function rules()
    {
        return [
            'username' => 'required|alpha_dash',
            'email'    => 'required|email',
            'password' => 'required|between:8,20|confirmed',
            'age'      => 'required|integer|max:60'
        ];
    }

    public function authorize()
    {
        if($this->age > 15)
            return true;
        else 
            return false;
    }

    public function forbiddenResponse()
    {
        return Response::make('You are too young to register!',403);
    }

}

Note:
Please do not consider the authorize method as some kind of a catch-all validation method for the custom validation rules. In the above code, I have used the age example just to show you how FormRequest class behaves when we return a true or a false value from authorize method. You should always use the authorize method for authorization checks like: checking a role, a permission or the ownership of a user on the resource he / she is trying to access.

Now, in the authorize method we are checking if the user has an age greater then 15, if so, he/she will be allowed to register. Otherwise, a forbidden response will be sent back to the user. The forbiddenResponse method can be used to override the default forbidden response.

Here is the code for the views:

<!-- file:file: resources/views/master.blade.php -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Laravel 5 New Awesome Form Validation</title>
    {{HTML::style('assets/css/bootstrap.css')}}
    {{HTML::style('assets/css/style.css')}}
</head>
<body>
    <div class="container">
        <div class="row">
            @yield('main')
        </div>
    </div>
</body>
</html>
<!-- file: resources/views/register.blade.php -->
@section('main')
    <div class="col-md-8 col-md-offset-2 form-content">
        <h3 class="heading">Laravel 5 Validation</h3>
        @if(Session::has('success'))
            <div>
                <p class="alert alert-success">{{Session::get('success')}}</p>
            </div>
        @endif
            @foreach($errors->all() as $error)
                <p class="alert alert-danger">{{$error}}</p>
            @endforeach
        {{Form::open(['route'=>['user.save'],'class'=>'form form-horizontal','style'=>'margin-top:50px'])}}
        <div class="form-group">
            {{ Form::label('username','User Name:',['class'=>'col-sm-3 control-label']) }}
            <div class="col-sm-8">
                {{ Form::text('username',Input::old('username'),['class'=>'form-control']) }}
            </div>
        </div>
        <div class="form-group">
            {{ Form::label('age','Age:',['class'=>'col-sm-3 control-label']) }}
            <div class="col-sm-8">
                {{ Form::text('age',Input::old('age'),['class'=>'form-control']) }}
            </div>
        </div>
        <div class="form-group">
            {{ Form::label('email','User Email:',['class'=>'col-sm-3 control-label']) }}
            <div class="col-sm-8">
                {{ Form::text('email',Input::old('email'),['class'=>'form-control']) }}
            </div>
        </div>
        <div class="form-group">
            {{ Form::label('password','Password:',['class'=>'col-sm-3 control-label']) }}
            <div class="col-sm-8">
                {{ Form::password('password',['class'=>'form-control']) }}
            </div>
        </div>
        <div class="form-group">
            {{ Form::label('password_confirmation','Confirm Password:',['class'=>'col-sm-3 control-label']) }}
            <div class="col-sm-8">
                {{ Form::password('password_confirmation',['class'=>'form-control']) }}
            </div>
        </div>
        <div class="text-center">
            {{Form::submit('Register',['class'=>'btn btn-default'])}}
        </div>
        {{Form::close()}}
    </div>
@stop

Note
The html package will be removed from the base in Laravel 5. But, you can install it manually, add the following line in the require section of the composer.json file of your Laravel 5-dev installation and run composer update:

"illuminate/html": "4.3.*"

Try it out

You can download the code by using the following link:

Download Source Code

After downloading the repository, extract the contents and run the composer update from within the extracted directory. After the composer installation process, you can serve the application by running the ./artisan serve command from the same directory.

A Final Note

Laravel 5 is scheduled to be released in November. The documentation on the new features is not complete — or I would say — is not available yet. So, if I am missing something here, do inform me I will update this article accordingly.

14 thoughts on “Laravel 5 New Awesome Form Validation”

  1. Nicely done article, was eager to find out how laravel will be standardizing validation since I’ve been working on validation on my app

    Something that I don’t clearly understand thought is how would you handle flow of the app if validation fails or not throught the controller itself. Does not feel right to couple flow of my app i the formResponse class! What if I have a form with either Ajax or post requests? One would need a formResponse with a redirect and one with sending back response in json?

    1. IMHO, form requests are like validation/authorization filters that we can apply on any controller method / route closure just by type hinting the method parameters. The flow of the application is not going to be affected in most of the cases, since form requests are completely customizable. Moreover, they are intelligent enough to figure out the type of a Response i.e, JSON or a Redirect needed for a perticular request. Thanks!

  2. Although you note above that “The html package will be removed from the base in Laravel 5.”, the download appears to have it installed. However, when I run the code as is, I get the form with no styling. Does this package need to be re-installed? Thank you.

    1. Yes, the illuminate/html is removed from laravel 5. It was only installed because I had added it as a dependency in the composer.json file for your convenience. Regarding ‘no styling’ please replace {{}} surrounding the HTML::style method calls with {{!! !!}} (see my reply to JJ). I hope this will solve the issue. Thanks!

  3. Just downloaded the code and gave it a whirl however it’s broken. The HTML and form tags generated using the HTML:: and Form:: classes are all being escaped, meaning the generated HTML and form tags are actually being embedded into the page rather than rendered by the browser. Any idea why?

    1. Hi, according to this commit, the behaviour of {{ }} constructs has been changed. From now on {!! !!} construct will be used to output raw data. I have pushed the relevant changes to the repository. Please re-download and try. It should work as expected. Thanks!

        1. According to Jeffrey Way, this syntax will tell you that you are doing something really dangerous as outputting raw data.

    1. There can be two possible approaches to this i.e. you can use two separate FormRequest classes or you can merge the functionality into one FormRequest and IMHO it is perfectly alright. The following code is doing this in one class:

      <?php namespace App\Http\Requests;
      
      use App\Link;
      use App\Http\Requests\Request;
      
      class LinkFormRequest extends Request {
      
          /**
           * rules to validate this form request
           * @var [type]
           */
          public $rules = [
              'url' => 'required|url|unique:links,url',
              'title' => 'required|max:255',
              'description' => 'required|max:255'
          ];
          
          /**
           * Get the validation rules that apply to the request.
           *
           * @return array
           */
          public function rules()
          {
              if($id = $this->id)
              {
                  $this->rules['url'] .= ','.$id;
              }
      
              return $this->rules;
          }
      
      }

      This is actually a working code from my current application that I am creating for my next tutorial on Laravel 5. The rules function checks for the existence of $id parameter and if it does, I am appending it to the url rule for exclusion.

      Usman.

  4. for $this->id to work I had to put in Form::hidden(‘id’) on my edit blade. This doesn’t seem great, is there a better way?

    1. Dan, I assumed that the id is present as a route parameter, for instance: Route::get('/resource/{id}/edit','SomeResourceController@edit').

      You can replace it with a slug or uuid whatever you are using to identify the resource.

      The base Request class implements a magic method, that will get the route parameter for us. So, there is no need for passing the id to the form as a hidden field.

      Please refer here: Request Class.

      I hope this will help.

        1. @Dan thank you for the tip about getting the id for RESTful resource routes, I’ve just spent ages Googling trying to find out how to pick up the id so I can use same Form Request for insert and edit as my forms are so similar.

          Thanks also to @Usman Riaz for the article

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.