Laravel Validation 101, Controllers, Form Requests, and Rules

Laravel Validation 101, Controllers, Form Requests, and Rules

A core part of any project is understanding how to validate the incoming request from your users and in this tutorial let’s look at how we can setup validation with our controllers, form requests, and rules.

Working with Controllers

By default, all Laravel controllers that extend from the Base Controller inherit the ValidatesRequests trait.

The ValidatesRequests trait gives you access to the validate method in your controller methods.

For example, say the user is creating an Article:

They may be entering a name, and some content for this article, along with a date to publish. Your controller validation may look like one of these, which are all valid:

public function store(Request $request) {
    $this->validate($request, [
        'name' => 'required|string',
        'body' => 'required|string',
        'publish_at' => 'required|date_format:Y-m-d H:i:s'
    ]);

    // The request validated and this code will get run
    ...
}

public function store(Request $request) {
    // Note: the array syntax is also available
    $request->validate([
      'name' => ['required', 'string'],
      'body' => ['required', 'string'],
      'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
    ]);

    // The request validated and this code will get run
    ...
}

public function store() {
    request()->validate([
      // Validation Rules...
    ]);

    // The request validated and this code will get run
    ...
}

public function store() {
    $this->validate(request(), [
      // Validation Rules...
    ]);

    // The request validated and this code will get run
    ...
}

It should also be noted, that the response to the validate method will be the data that was validated:

public function store(Request $request) {
    $validatedData = $request->validate([
      'name' => ['required', 'string'],
      'body' => ['required', 'string'],
      'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
    ]);

    // I know that the only keys in validated data are the ones that were successfully validated, i.e.: name, body, published_at
    $this->articleService->store($validatedData);
}

Alternatively, you can do this too:

public function store(Request $request) {
    $request->validate([
      'name' => ['required', 'string'],
      'body' => ['required', 'string'],
      'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
    ]);

    // I know that the only keys in validated data are the ones that were successfully validated, i.e.: name, body, published_at
    $this->articleService->store($request->only('name', 'body', 'published_at'));
}

A list of the available validation rules can be found here.

Working with Form Requests

If you would like to extract your validation logic away from your controllers, or you would like to do authorization and validation at the same time, Laravel makes the Form Request class available to you.

You can make a form request class using one of the many built-in Laravel generators:

php artisan make:request StoreArticleRequest

This will make you the following class in app/Http/Requests by default:

class StoreArticleRequest extends FormRequest
{

    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            //
        ];
    }
}

Form request classes comes with 2 methods, authorize and rules.

Authorize

Authorize lets you prevent the controller code from running if this method returns false. Laravel will throw a 401 unauthorized exception.

An example usage might be:

public function authorize()
{
    return $this->user()->can('create articles');
}

Rules

The rules method will return our array of rules that was in our controller validate method;

public function rules()
{
    return [
      'name' => ['required', 'string'],
      'body' => ['required', 'string'],
      'publish_at' => ['required', 'date_format:Y-m-d H:i:s'],
    ];
}

Note: You can type-hint any dependencies needed into the rules method.

So, how do we use this class? We type-hint it right into our controller method:

public function store(StoreArticleRequest $request) {
    // Validation and Rules passed
}

If the validation fails, the user will be redirected back with the errors flashed to the session. If it is an AJAX call, it will be a 422 Unprocessable Entity response.

You may get the validated data just like before:

public function store(StoreArticleRequest $request) {
    $validatedData = $request->validated();

    // Insert into database
    ...
}

Working with Custom Rule Classes

If there is a validation rule that does not exist as part of the available built-in validation rules, you can create a custom one:

Again, using one of Laravel’s make commands:

php artisan make:rule IsValidStateInUSA

This will make you the following class in app/Rules by default:

class IsValidStateInUSA implements Rule
{

    public function passes($attribute, $value)
    {
        //
    }

    public function message()
    {
        return 'The validation error message.';
    }
}

Your passes method holds the logic for allowing this validation method through. The message method is the message that gets returned if it fails.

Using the example of Is a valid State in the USA, we would need the item passed in to be one of 50 different state codes. It could look like this:

class IsValidStateInUSA implements Rule
{

    public function passes($attribute, $value)
    {
        return in_array(strtoupper($value), [
          'AL',
          'AK',
          'AZ',
          ...
        ]);
    }

    public function message()
    {
        return 'This is not a valid state code in the USA.';
    }
}

Then, to use this in your controller or form request, you just add it to the list of validation rules for that key:

public function store(Request $request) {
    $request->validate([
      'state' => ['required', new IsValidStateInUSA],
    ]);

    ...
}

Handling Validation Errors

When validation fails, Laravel will automatically redirect back, with an $errors variables flashed in the session.

You can easily create a file called messages.blade.php and include in your master layout file like this:

@if($errors->any())
    <div class="alert alert-danger" role="alert">
        <button type="button" class="close" data-dismiss="alert" aria-label="Close">
            <span aria-hidden="true">×</span>
        </button>

        @foreach($errors->all() as $error)
            {{ $error }}<br/>
        @endforeach
    </div>
@endif

Which will display all errors flashed to the session, or if you prefer to handle them one by one on a per blade file basis, you can use the blade @error helper:

@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

The $message variable will be provided to you inside the @error tag.

Customizing Error Messages

When working with the validate method, you can override the messages with the third parameter:

public function store(Request $request) {
    $this->validate($request, [
        'name' => 'required|string',
        'body' => 'required|string',
        'publish_at' => 'required|date_format:Y-m-d H:i:s'
    ], [
      'name.required' => 'A article name is required',
      'body.required'  => 'A article body is required',
      'publish_at.date_format' => 'The publish at date is not in the correct format.'
    ]);

    // The request validated and this code will get run
    ...
}

When working with a form request class, a messages function can also be implemented to customize the error message for each specific rule:

public function messages()
{
    return [
        'name.required' => 'A article name is required',
        'body.required'  => 'A article body is required',
        'publish_at.date_format' => 'The publish at date is not in the correct format.'
    ];
}

For the most up to date documentation and a much more in-depth look at validation, visit the Laravel Documentation.

Filed in: News
Laravel News Partners

Laravel Jobs

Senior Laravel Developer (Remote)
Remote (US or Canada)
Aryeo
Medior Laravel Backend Developer (The Netherlands/Dutch only)
Nijmegen, The Netherlands
BigSpark B.V.
Laravel Developer
Remote
Enjin
Senior Laravel Backend Engineer
Remote, USA Only
Kittyhawk.io, Inc.
Senior Laravel Developer (b2b saas)
Remote
Ulobby

Newsletter

Join 31,000+ others and never miss out on new tips, tutorials, and more.