Build validation that you can be proud of in Laravel
Tags: validation, laravel, php
Published: 2022-05-16 13:35:57

---
title: Build validation that you can be proud of in Laravel
published: true
date: 2022-05-08 13:22:00 UTC
tags: validation, laravel, php
canonical_url: https://dberri.com/build-validation-that-you-can-be-proud-of-in-laravel
---

Today I wanted to bring you the topic of validation in the backend using Laravel. For me, this is part of what makes this framework so elegant and fluent. Even though it allows you validate your input data using multiple approaches, it gives you guard rails to ensure that it will work well and will stay organized as your application grows.

Let's jump into the code. I created a starter project that you can use to follow along. Just clone the repo from [here](https://github.com/dberri/laravel-validation-demo/tree/starter). It's a simple CRUD project with a Rest API to show, store, update and remove bird sightings from the application. Currently it doesn't have any validation at all. Whatever it receives from the clients, it will try to store or update in the database, which is can be very bad even if you have validation on the client code. Let's start fixing that with [Form Requests](https://laravel.com/docs/9.x/validation#form-request-validation).

## Form Requests

To be honest, if you have only a couple of rules, you might not need a Form Request, you can just validate the request inside the controller method like:

```php
// app/Http/Controllers/BurdSightingController.php

public function create(Request $request)
{
    $request->validate([
        'common_name' => 'required|string|max:255',
    ]);

    $sighting = BirdSighting::create($request->validated());
    return response()->json($sighting, 201);
}

```

But if you want a cleaner interface with the controller and a special place where all the validation happens, then you can create a FormRequest using an artisan command:

```sh
php artisan make:request CreateBirdSightingRequest

```

This will create the following file:

```php
// app/Http/Requests/CreateBirdSightingRequest.php

class CreateBirdSightingRequest extends FormRequest
{
    public function authorize()
    {
        return false;
    }

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

```

The `authorize` method can be used to determined if the current user is authorized to perform that operation, so you can, for example, make use of Gates and Policies or even simpler rules like if the user has a specific property. Make sure to return a boolean in that method: true means that the user is authorized and false means the user is not authorized and will respond with a 403 HTTP status response.

The `rules` method is where the actual validation rules go. Laravel has [dozens of built-in](https://laravel.com/docs/9.x/validation#available-validation-rules) validation rules to add here. So, in our case, we want to validate fields in order to create a bird sighting:

```php
    public function rules()
    {
        return [
            'common_name' => 'required|string|max:255',
            'species' => 'required|string|max:255',
            'sighted_at' => 'required|date',
            'quantity' => 'nullable|integer|min:1',
            'latitude' => 'required|numeric',
						'longitude' => ['required', 'numeric'],
        ];
    }

```

You will notice that for `longitude` I used a different syntax. Laravel supports both strings with rules a separated by `|`and arrays with rules are separated by commas. As far as I know, the string syntax can only be used with built-in rules. If you create a custom validation rule (and I'll show you how) then you have to use the array syntax.

## Custom validation messages

Form Requests also allow you to add custom validation messages for each field and rule. TO do that you need to override the `messages` method in the Form Request you created, for example:

```php
    public function messages()
    {
        return [
            'common_name.required' => 'A common name for the bird you saw is required',
            'sighted_at.required' => 'An approximate date and time for the sighting is required',
        ];
    }

```

These messages will override the default validation message for the `required` rule which you can find in `resources/lang/validation.php`.

Finally, you have to use this Form Request in your method, so go back to the controller and replace `Request $request`with `CreateBirdSightingRequest $request`

```php
    public function create(CreateBirdSightingRequest $request)
    {
        $sighting = BirdSighting::create($request->validated());
        return response()->json($sighting, 201);
    }

```

You will also notice that we replaced `$request->all()` with `$request->validated()` to retrieve the validated fields from the request.

Now if you send data to that endpoint and it does not conform to the rules you added, you'll get a 422 HTTP status response with the error messages for each field.

## Organizing rules

Now you will need another form request for the `update` method which might have a different set of rules and speaking of set of rules, when the application starts to grow and rules start to get too "scattered" in the form request, we can create rule classes that make those sets reusable and more legible. I like to create a `Rules` directory inside `app/` and create a class that will provide the rules I need:

```php
// app/BirdSightingRules.php

class BirdSightingRules
{
    public static function birdRules()
    {
        return [
            'common_name' => 'required|string|max:255',
            'species' => 'required|string|max:255',
            'quantity' => 'nullable|integer|min:1',
        ];
    }

    public static function locationRules()
    {
        return [
            'sighted_at' => 'required|date',
            'latitude' => 'required|numeric',
            'longitude' => ['required', 'numeric'],
        ];
    }

    public static function updateRules()
    {
        return [
            // 
        ];
    }
}

```

Then back in the form request:

```php
    public function rules()
    {
        return array_merge(
            BirdSightingRules::birdRules(),
            BirdSightingRules::locationRules()
        );
    }

```

## Custom rules

Laravel has so many built-in rules that it might take some time for you to need to build a custom rule, but sometimes your application might just have a specific trait that requires a custom validation rule. If that's the case, artisan has you covered:

```sh
php artisan make:rule SightedInMarch

```

It will create a file under `app/Rules` which contains a custom rule with the following methods:

```php
// app/Rules/SightedInMarch.php

class SightedInMarch implements Rule
{
    public function passes($attribute, $value)
    {
        //
    }

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

```

Inside the `passes` method you can create your custom rule which needs to return a boolean and inside `message` you just need to return a string with an explanation of why it failed the validation:

```php
    public function passes($attribute, $value)
    {
        return preg_match('/^[0-9]{4}-03-[0-9]{2}/', $value);
    }

    public function message()
    {
        return 'Why are you watching birds in March?';
    }

```

Then you go back to you set of rules and use this custom rule with the `sighted_at` attribute:

```php
// app/Rules/BirdSightingRules.php

    public static function locationRules()
    {
        return [
            'sighted_at' => ['required', 'date', new SightedInMarch],
            'latitude' => 'required|numeric',
            'longitude' => ['required', 'numeric'],
        ];
    }

```

Now all form requests that use `locationRules` will automatically validate the `sighted_at` attribute using that new custom rule. And with that, we wrap another article. See you on the next one!