One of the most important aspects of any project is validating data that are coming into your system. It’s essential to provide the proper validation rules that will ensure your system will be secure and invulnerable to attacks.

In this tutorial, we’ll go over how you can validate a form in Laravel using the available methods and create custom validation rules in Laravel.

Prerequisites

We’ll quickly go over how to install and set up a Laravel project. If you already have one ready, you can skip this section.

First, make sure you have Composer installed. Then, install a new Laravel project with the following command:

composer create-project laravel/laravel validation-tutorial

Then, change the directory to the project created and run the serve

cd validation-tutorial
php artisan serve

And with that, the server will start running and you can access it on localhost:8000 or the port specified when running the serve command.

If you get an error about permissions, change the `storage` directory permissions to be readable.

Create a Form

Create the file resources/views/form.blade.php with the following content:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Register</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
</head>
<body class="bg-light">
    <div class="container bg-white shadow mx-auto mt-5 rounded">
        <form method="POST" action="" class="py-3 p-5">
            @csrf
            <h1 class="text-center">
                Register
            </h1>
            <div class="mb-3">
                <label for="name" class="form-label">Name</label>
                <input type="text" name="name" class="form-control" id="name" aria-describedby="nameHelp" required>
                <div id="nameHelp" class="form-text">Please enter both first and last name</div>
            </div>
<div class="mb-3">
                <label for="date_of_birth" class="form-label">Date of Birth</label>
                <input type="date" name="date_of_birth" class="form-control" id="date_of_birth">
            </div>
            <div class="mb-3">
                <label for="email" class="form-label">Email address</label>
                <input type="email" name="email" class="form-control" id="email" required>
            </div>
            <div class="mb-3">
                <label for="password" class="form-label">Password</label>
                <input type="password" name="password" class="form-control" id="password" required>
            </div>
            <div class="mb-3">
                <label for="password_confirmation" class="form-label">Password Confirmation</label>
                <input type="password" name="password_confirmation" class="form-control" id="password_confirmation" required>
            </div>
            <div class="text-center">
                <button type="submit" class="btn btn-primary">Submit</button>
            </div>
        </form>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
</body>
</html>

This will create a page with a form that has 5 input fields: Name, Date of Birth, Email, Password, and Password Confirmation. Note that for easy styling I’ve added the Bootstrap CDN for the CSS and JS files, however, it’s better to download them and use them in your project.

The current form just currently accepts the input. However, we haven’t added the `action` which will accept the input, and we aren’t showing any error messages or success messages from the server.

Next, we’ll create a controller that will handle all the requests related to the form. Run the following command to do that:

php artisan make:controller FormController

This will create a new controller in app/Http/Controllers/FormController.php. Open it and a new function showForm that just returns the view we just created:

public function showForm () {
    return view('form');
}

Finally, let’s create the new route in `routes/web.php`:

//add the following in the beginning
use App\Http\Controllers\FormController;
Route::get('/register', [FormController::class, 'showForm']);

Let’s see our form now. Start the server:

php artisan serve

And go to localhost:8000/register. You’ll see the form we created.

Create The Form Action Route

We’ll create now the route that will accept the inputs and validate them. In app/Http/Controllers/FormController.php, create a new function called register:

public function register(Request $request) {
}

Inside the function, we’ll start by validating the input. To validate the input, we use the Validation facade. The Validation facade has a static method called `validate`, which takes 2 parameters. The first is the array of data to validate. The second is the array of validation rules to apply to each of the data.

The array of validation rules consists of key-value pairs, where the key is the name of the field in the input received (for example, `name`), and the value is a string consisting of the validation rules to apply. For example, to ensure that `name` is always present in the parameters, we use the following key-value pair in the array of the second parameter:

‘name’ => ‘required’,

What if we want to use more than one validation rule? We want to ensure that not only is the name present, but it at least has 1 character in it. To separate validation rules in the value string, the separator “|” is used:

‘name’ =>required|min:1,

Where min ensures that if the name is a string, it at least has 1 character.

Another way to use multiple validation rules is using an array for the value rather than a string:

‘name’ => [‘required’, ‘name’],

Let’s start by first using the validate function, passing it the data received from the form using $request->all(), then we’ll add the validation rules one by one to the second parameter array:

//validate request
Validator::validate($request->all(), [
]);

We’ll first add validation rules for the Name input. The Name input is `required` and should be at least 1 character long:

'name' =>  'required|min:1',

Second, the Date of Birth input. This input is optional, but when it’s present in the request’s body, it should be a date. So, to make sure it’s optional we use the validation rule nullable, and to make sure it’s a date we use the date validation rule:

'date_of_birth'     =>  'nullable|date',

Third, the Email input. This input is required and it should be validated to be an email. To ensure that the value is an email, we use the email validation rule:

'email'             =>  'required|email',

And finally, the Password and Password Confirmation inputs. The password is required and should be 8 characters long. So, we’ll use the required and min validation rules we used earlier. However, it should also be confirmed in the Password Confirmation input. To ensure that, we use the confirmed validation rule. This validation rule searches for another input with the name {input}_confirmation where {input} is the input name of the input that has the validation rule confirmed. So, in this example, it will look for password_confirmation. If the password input’s name was new_password, it will look for new_password_confirmation. For this reason, when using the confirmed validation rule, make sure that the confirmation field has that name format.

So, the validation for the Password and Password Confirmation inputs is:

'password'          =>  'required|min:8|confirmed',

Note that since we’re using `confirmed` there’s no need to add any validation rules specific to Password Confirmation input, as the `confirmed` rule will check that the Password Confirmation input is present and has the same value as `password`, and if the Password input is not valid then the Password Confirmation is ultimately not valid as well.

That’s all the validation rules we need for now. The `validate` method will take the data in the first parameter and apply the validation rules from the second parameter. If there are any validation errors, the `validate` method will redirect back to the form with the errors. So, in our code after executing the `validate` method, we can be sure that everything after that will be executed if and only if the data is valid.

After the form, you can run any logic you need with it that can only be run if the data is valid. For example, save the user in the database or send an email. Here, we’ll just add a success key in the session and redirect back to the form:

//all valid
//return success message
session()->flash('success', true);
return redirect()->back();

We’ll use the `success` key in the session to determine whether to show a success message. Note that the `flash` method ensures that this value appears in the session only in the next request, then the value will be automatically removed from the session.

In the showForm method, change the code to the following:

$success = session()->get('success');
return view('form', ['success' => $success]);

This takes the success value from the session and passes it as a variable to the form view. Now, add the following nested inside the form element under the h1 element in resources/views/form.blade.php:

@if ($success)
<div class="alert alert-success">
      You have registered successfully!
</div>
@endif

This checks if the `success` variable evaluates to true, it will show a success message to the user that their registration was successful.

What about error messages? How do we show them in our form?

You can check if any element has an error message using Blade’s `@error` directive. If there’s an error on the input, everything inside the directive’s block will show. If not, nothing will.

For example, to add the class is-invalid to the name input if it’s not valid:

<input type="text" name="name" class="form-control @error('name') is-invalid @enderror" id="name" aria-describedby="nameHelp" required>

To show the error message, inside the @error directive you can reference a $message variable, which will be the error of the input:

@error('name')
	<span class="invalid-feedback">{{ $message }}</span>
@enderror

So, let’s make the changes to all the input fields. We’ll add the class is-invalid if the input has errors, and we’ll add the error message below the input field. We’ll make the change to the name, date_of_birth, email, and password fields:

<div class="mb-3">
                <label for="name" class="form-label">Name</label>
                <input type="text" name="name" class="form-control @error('name') is-invalid @enderror" id="name" aria-describedby="nameHelp" required>
                <div id="nameHelp" class="form-text">Please enter both first and last name</div>
                @error('name')
                    <span class="invalid-feedback">{{ $message }}</span>
                @enderror
            </div>
            <div class="mb-3">
                <label for="date_of_birth" class="form-label">Date of Birth</label>
                <input type="date" name="date_of_birth" class="form-control @error('date_of_birth') is-invalid @enderror" id="date_of_birth">
                @error('date_of_birth')
                    <span class="invalid-feedback">{{ $message }}</span>
                @enderror
            </div>
            <div class="mb-3">
                <label for="email" class="form-label">Email address</label>
                <input type="email" name="email" class="form-control @error('email') is-invalid @enderror" id="email" required>
                @error('email')
                    <span class="invalid-feedback">{{ $message }}</span>
                @enderror
            </div>
            <div class="mb-3">
                <label for="password" class="form-label">Password</label>
                <input type="password" name="password" class="form-control @error('password') is-invalid @enderror" id="password" required>
                @error('password')
                    <span class="invalid-feedback">{{ $message }}</span>
                @enderror
</div>

The last thing we need to do is create a new route for the submission in routes/web.php:

Route::post('/register', [FormController::class, 'register'])->name('register');

Notice that we gave the route the name register. Then, change the action attribute of the form element in resources/views/form.blade.php:

<form method="POST" action="{{ route('register') }}" class="py-3 p-5">

Our form is now ready to accept, validate data and show errors or success messages. Let’s test it out. If your server is not running, make sure to run it again.

Go to localhost:8000/register and try at first to submit all the data correctly. You will see a success message on submission.

Next, try to submit the form with data that will cause validation errors. For example, make the password less than 8 characters or enter a password confirmation that’s not similar to the password. You’ll see that after submission the Password field will have a red border and below it in red is the error message:

We can see now that the validation is working correctly! Since there was a validation issue, everything after the `validate` method didn’t execute, which is evident since we don’t see a success message. So, the `success` key was not written in the session and no message was shown because of that.

Show Old Values

When a user enters invalid data, a lot of times it’s either because they made a mistake or they’re not aware of what makes the data invalid. So, it’s good to show the old values that the user enters before the form submission. To get the old value of the input, we use the `old` method passing it the name of the input.

We’ll add that to the Name input:

<input type="text" name="name" class="form-control @error('name') is-invalid @enderror" id="name" aria-describedby="nameHelp" value="{{ old('name') }}" required>

The Date of Birth Input:

<input type="date" name="date_of_birth" class="form-control @error('date_of_birth') is-invalid @enderror" id="date_of_birth" value="{{ old('date_of_birth') }}">

And finally, the Email input:

<input type="email" name="email" class="form-control @error('email') is-invalid @enderror" id="email" required value="{{ old('email') }}">

The password’s old value should not be shown. Now, try to submit the form again with some validation issues. You’ll see that the values you entered in the form before submission are repopulated in the form:

Create Custom Rule

Laravel provides out of the box a wide variety of rules that you can use for almost any case you need. However, there will be certain cases where these rules are not enough to validate the data you’re receiving. For that reason, you might need to create a custom rule that not only runs the validation you need but also behaves the same way the other validation rules built in Laravel do.

We’ll create a simple custom rule called Name that will check if the name has both a first and a last name separated by a space.

To create a custom rule, run the following command:

php artisan make:rule Name

This will create the new class Name in app/Rules/Name.php. This class will have two methods. The first one is called passes which receives the input name as a first parameter and the input’s value as a second parameter, then runs the validation logic needed and returns a boolean that determines whether the input value is valid or not. The second one is called message which returns the validation message that should show if the input is not valid.

In our example, in the passes method we’ll check if the value of the name input has at least 2 words separated by a space:

public function passes($attribute, $value) {
        return count(explode(" ", $value)) > 1;
}

And in the message method we’ll return the following:

public function message() {
        return 'Name should have both first and last name.';
}

We’ll use the newly created rule in the validation of the form. To do that, we’ll need to change the value of the name key in the second parameter of the validate method to an array and add our new rule to the end of it:

'name'              =>  [
        'required',
        'min:1',
        new Name
],

Now, go to the form (start the server first if it isn’t running) and try to submit the form with just a first name. You’ll see the error message that we return in the message method under the name input:

And if you enter a first and last name separated by a space, you’ll see a success message. This means that our validation rules are all working correctly.

Conclusion

In this tutorial, we went over how to use validation in Laravel using the built-in rules and using a custom rule. Validation is very important, as it ensures that all the data that are going into your system are of a specific necessary format or are secure. That’s why it’s essential to understand and master validation.