Laravel Form Request Demystified

Laravel provides an incredible way to handle form requests. Laravel form request handling is a very crucial part of any application.

By using Laravel for modern web application development we can break down our code into smaller chunks, which reduce code clutter and makes the code easier to read.

You might already know that there are many ways to handle a form request for validation purpose. In this post, we will be looking at how we add form request validations, also how we can refactor our validation code to a separate class.

Laravel Form Request Validation in Controllers

One of the common practice is to handle validation for the incoming request within your controller and most of the time people do that. There is nothing wrong with it.

For example, we have a Customer model and want to validate the name and email while creating a new customer. Here’s how our validator looks like in CustomerController.

namespace App\Http\Controllers;
use App\Customer;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
class CustomerController extends Controller
{
    public function create()
    {
        return view('customers.add');
    }
    public function store(Request $request)
    {
        // Validate incoming request
        $validator = Validator::make($request->all(), [
            'name'      =>  'required|max:255',
            'email'     =>  'required|email|unique:customers',
        ]);
        if ($validator->fails()) {
            Session::flash('error', $validator->messages()->first());
            return redirect()->back()->withInput();
        }
        // validation passed, store customer into database
    }
}

You can do this way, there is nothing wrong with it and it will definitely work. However, the store() method of CustomerController could grow as the application requires more complex logic.

Well, this is a bad practice from my point of view, because handling validation is not the job of a controller. Controller job is to receive a route request and return a response for that request.

Above code example, also break the Single Responsibility Principle. We know that the requirements always change over time so does the class responsibilities. So having many responsibilities in a single class can make it very difficult to make this controller.

Creating Custom Requests in Laravel

Laravel provides a separate class for handling the request validation and its sole purpose is to handle the validation of a request. We can create a request class using below artisan command:

php artisan make:request StoreCustomerRequest

After running above command, Laravel will generate a new request class in app/Http/Requests folder. Here is how our newly created class looks like.

namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreCustomerRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return false;
    }
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            //
        ];
    }
}

As we can see that, StoreCustomerRequest class contains two methods authorize() and rules().

  • authorize() : This method is used for any authorization logic which need to be applied to the coming request.
  • rules() : Here you have to define all the validation rules for incoming request, you can also create your own custom rules as well. You can find the full list of rules available in the Laravel on Laravel Validation documentation.

Now we will update our newly create StoreCustomerRequest class. First thing return true from the authorize() method as we want to authorize this form request.

Second, we will update rules() method by adding validation rules for name and email fields of Customer model.

Your updated class will look like below:

namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreCustomerRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }
    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'name'      =>  'required|max:255',
            'email'     =>  'required|email|unique:customers',
        ];
    }
}

Now we can update our controller to use the StoreCustomerRequest class for validation and remove the validation we added earlier from controller. Here is how your CustomerController will look like.

namespace App\Http\Controllers;
use App\Customer;
use App\Http\Requests\StoreCustomerRequest;
use Illuminate\Support\Facades\Validator;
class CustomerController extends Controller
{
    public function create()
    {
        return view('customers.add');
    }
    public function store(StoreCustomerRequest $request)
    {
        $validator = $request->validated();
        if ($validator->fails()) {
            Session::flash('error', $validator->messages()->first());
            return redirect()->back()->withInput();
        }
        // validation passed, store customer into database
    }
}

Now our controller is clean and slim, our controller doesn’t have to worry about the validation logic. We have one request class with only one responsibility to handle validation and let the controller deal with request and response.

Creating Custom Messages for Validation Rules

In your request Laravel provide another method called messages() where you can pass your own custom messages for validation rules. Just add below code to your StoreCustomerRequest class and you will have custom messages for your validation rules.

public function messages()
    {
        return [
            'name.required'     => 'Name is required!',
            'name.max'          =>  'Name can not be more than 255 characters!'
            'email.required'    => 'Email is required!',
            'email.email'       => 'Email field needs to be a valid email address!',
            'email.unique'      => 'Email already in use. Please enter a unique email address.!',
        ];
    }

Now you have customized the validation error messages and if validation fails, the above messages will be displayed to the user.

Showing Validation Error Messages

Upon any validation error, users will be redirected back to the last location and Laravel will flash all validation messages to the session. To show the flash messages, you have to add below code in the view in our case it’s add view in customers folder.

<h1>Add Customer</h1>
@if ($errors->any())
        <div class="alert alert-danger">
            <ul>
            @foreach ($errors->all() as $error)
                    <li>{{ $error }} </li>
            @endforeach
            </ul>
        </div>
@endif

Conclusion

Laravel Form Request is a great way to trim down our controller and keep everything separate. It seems unnecessary to make a separate class for validation, but by adding an extra validation class we can refactor our controller for any future changes and make it more manageable.