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.