Customizing Validation Rules Using Laravel

7 min read

  • Languages, frameworks, tools, and trends

While you are working with user input and forms, validating the common fields and extracting the exact validation rules makes the code easier to maintain and read. You can make the code powerful by using the Laravel custom validation rule with parameters. This streamlines the flow between the logic and code wherever required.

Laravel offers various convenient validation rules that can be applied to the data if values are specific to a database table. In this insightful article, we will cover all the validation rules in detail to make you familiar with the features of these validation rules.

Starting validation

To brush up on your knowledge, let’s consider an example of form validation and portray the error message to the user. We will walk you through this overview so you can have a complete understanding of how to validate an incoming data request in Laravel.

How to start Laravel custom validation rule..webp

Defining different routes

Let’s take an example where we define the following routes in our ‘routes/web.php’ file.

use App\Http\Controllers\PostController;
 
Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);
PHP

In the above code, the ‘GET’ route will depict a form to create a new post. The ‘POST’ route shares the new post in the database.

Creating a new controller

Now let’s understand how an easy controller handles the incoming data requests regarding these routes.

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
 
class PostController extends Controller
{
   /**
    * Show the form to create a new blog post.
    *
    * @return \Illuminate\View\View
    */
   public function create()
   {
       return view('post.create');
   }
 
   /**
    * Store a new blog post.
    *
    * @param  \Illuminate\Http\Request  $request
    * @return \Illuminate\Http\Response
    */
   public function store(Request $request)
   {
       // Validate and store the blog post...
   }
}
PHP

Coding the validation logic

After creating a new controller, we are ready to prepare and implement the store method with the logic to validate the new post. To do this, we need to validate the methods provided by the ‘Illuminate\Http\Request’ object. If the validation rules pass, the code that has been prepared will execute normally. Apart from a successful run, if the validation fails, the ‘Illuminate\Validation\ValidationException’ is disowned and the error message is displayed.

If the validation fails during a traditional HTTP request, a direct response is sent to the previous URL. In the case of an XHR request, a JSON response that contains the error message is displayed.

Let’s go back to the store method and dive deeper into the validation approach.

/**
* Store a new blog post.
*
* @param  \Illuminate\Http\Request  $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
   $validated = $request->validate([
       'title' => 'required|unique:posts|max:255',
       'body' => 'required',
   ]);
 
   // The blog post is valid...
}
PHP

All the general validation rules are recorded properly. In case, the validation can’t perform, a proper response is automatically generated.

$validatedData = $request->validate([
   'title' => ['required', 'unique:posts', 'max:255'],
   'body' => ['required'],
]);
PHP

Nested attributes

You can specify the nested fields in your validation rules using the ‘dot’ syntax in case the incoming HTTP request includes the nested field data.

$request->validate([
   'title' => 'required|unique:posts|max:255',
   'author.name' => 'required',
   'author.description' => 'required',
]);
PHP

Alternatively, if the field holds a literal period, you can stop this from being analyzed as the ‘dot’ syntax by exiting the period using a backslash.

$request->validate([
   'title' => 'required|unique:posts|max:255',
   'v1\.0' => 'required',
]);
PHP

Validating a form request

This is the second phase of the entire process. Let us check out the different sub-processes.

Validating Laravel form request.webp

Creating & implementing form requests

You may implement a ‘form request’ for a more complicated Laravel custom validation rule. To build a form request class, it is necessary to use the Artisan CLI command:

php artisan make:request StorePostRequest
PHP

The form request class you prepared is placed in the ‘app/Http/Requests’ directory. Every form request comprises two methods:

  • Authorize
  • Rules
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
   return [
       'title' => 'required|unique:posts|max:255',
       'body' => 'required',
   ];
}
PHP

So how do you validate the rules? You have to type-hint the request on the controller method. The incoming form request will be validated prior to the controller method being implemented and it allows you to declutter your controller with the validation logic.

Let us understand this process with the code below.

/**
* Store a new blog post.
*
* @param  \App\Http\Requests\StorePostRequest  $request
* @return Illuminate\Http\Response
*/
public function store(StorePostRequest $request)
{
   // The incoming request is valid...
 
   // Retrieve the validated input data...
   $validated = $request->validated();
 
   // Retrieve a portion of the validated input data...
   $validated = $request->safe()->only(['name', 'email']);
   $validated = $request->safe()->except(['name', 'email']);
PHP

The after-hooks to form requests

When you want to add an ‘after’ validation hook to a form request, you can do that by using the ‘withValidator’ approach. This approach receives a completely constructed validator and allows you to call the sub-methods before the validation rules are analyzed.

/**
* Configure the validator instance.
*
* @param  \Illuminate\Validation\Validator  $validator
* @return void
*/
public function withValidator($validator)
{
   $validator->after(function ($validator) {
       if ($this->somethingElseIsInvalid()) {
           $validator->errors()->add('field', 'Something is wrong with this field!');
       }
   });
}
PHP

Discontinuing after the first validation failure

You can notify the validator to stop validating the attributes if there is a failure. You can do this by adding a ‘stopOnFirstFailure’ function to your request class.

/**
* Indicates if the validator should stop on the first rule failure.
*
* @var bool
*/
protected $stopOnFirstFailure = true;
PHP

Preparing a redirect route

As we discussed previously, a direct response is generated and sent back to the user to the previous location in case of a validation failure. Nonetheless, you can customize a redirect route by defining a ‘$redirect’ property.

/**
* The URI that users should be redirected to if validation fails.
*
* @var string
*/
protected $redirect = '/dashboard';
PHP

Passing the form requests

The form request class has an Authorize method using which you can evaluate when the authenticated user has permission to update a resource. For example, you can evaluate if the user has the authority to update a blog post or a comment. Here is the code fragment to better understand how to authorize the form request.

use App\Models\Comment;
 
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
   $comment = Comment::find($this->route('comment'));
 
   return $comment && $this->user()->can('update', $comment);
}
PHP

If this method returns False, then an HTTP response with a 403 status code is generated and your methods will not be executed. You can simply return the ‘true’ from the authorized method if you want to handle the authorization logic for requests in any other part of your application.

/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
   return true;
}
PHP

Input for validation

You can prepare any data from the request before you implement your validation rules. To do that, you have to implement the ‘prepareForValidation’ approach. The code fragment below shows how to do that.

use Illuminate\Support\Str;
 
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
   $this->merge([
       'slug' => Str::slug($this->slug),
   ]);
}
PHP

Conditionally adding the rules

Here is how you can create your Laravel validation custom rules and implement them. This step is fragmented into multiple small steps.

Adding Laravel validation custom rules..webp

Skipping validation when a field has a certain value

You can choose not to validate a field if it contains a value. To do so, you can use the ‘exclude_if’ rule.

In the example, there are two fields.

  • Appointment_date
  • doctor_name

These two fields won’t be validated if the ‘has_appointment field’ has a false value.

use Illuminate\Support\Facades\Validator;
 
$validator = Validator::make($data, [
   'has_appointment' => 'required|boolean',
   'appointment_date' => 'exclude_if:has_appointment,false|required|date',
   'doctor_name' => 'exclude_if:has_appointment,false|required|string',
]);
PHP

Validation with a condition

You can wish to add the validation rules based on complex logic according to a few conditions. To do this, you have to have two fields with values only when a different field is present. In the code below, we have created a validator with the static rules.

use Illuminate\Support\Facades\Validator;
 
$validator = Validator::make($request->all(), [
   'email' => 'required|email',
   'games' => 'required|numeric',
]);
PHP

Array validation with a complex conditions

If you do not know the index, you can validate a field based on another field in the same nested array. In such a scenario, you can have a second argument that becomes the current item in the array you want to validate.

$input = [
   'channels' => [
       [
           'type' => 'email',
           'address' => 'abigail@example.com',
       ],
       [
           'type' => 'url',
           'address' => 'https://example.com',
       ],
   ],
];
 
$validator->sometimes('channels.*.address', 'email', function ($input, $item) {
   return $item->type === 'email';
});
 
$validator->sometimes('channels.*.address', 'url', function ($input, $item) {
   return $item->type !== 'email';
});
PHP

Password validation

You can use Laravel’s password rule object to ensure that the passwords have a decent level of complexity.

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rules\Password;
 
$validator = Validator::make($request->all(), [
   'password' => ['required', 'confirmed', Password::min(8)],
]);
PHP

The password rule object enables you to customize the complexity needed for your application. You can have complex passwords that have these compulsory requirements:

  • 1 upper case letter
  • 1 lower case letter
  • 1 or 2 special symbol
  • Mixed casing
// Require at least 8 characters...
Password::min(8)
 
// Require at least one letter...
Password::min(8)->letters()
 
// Require at least one uppercase and one lowercase letter...
Password::min(8)->mixedCase()
 
// Require at least one number...
Password::min(8)->numbers()
 
// Require at least one symbol...
Password::min(8)->symbols()
PHP

Apart from the above method, you can have more security and complexity to your passwords using the ‘uncompromised’ method.

Password::min(8)->uncompromised()
PHP

If a password appears at least once in a data leak, it is treated as ‘compromised’. You can customize this constraint using the first argument of the ‘uncompromised method. Here is the code fragment to do so.

// Ensure the password appears less than 3 times in the same data leak...
Password::min(8)->uncompromised(3);
PHP

Creating Laravel custom validation rules

After these steps, you are ready to create customized validation rules. There are three broad categories under which you can customize them.

  • Rule objects
  • Closures
  • Implicit rules

Let’s have a quick walkthrough.

Creating Laravel custom validation rules..webp

Using the rule objects

Laravel offers a wide variety of useful validation rules and you can also customize some of your own if you wish to. One popular method of registering the custom validation rules is by using the rule objects.

If you wish to customize a new rule object, you can use the Artisan command. Let’s understand this approach by customizing a rule that determines if a string is uppercase. Laravel enables you to place the new rule in the ‘app/Rules’ directory.

php artisan make:rule Uppercase --invokable
PHP

Once you have created a new rule, you can define its behavior. A new rule object contains ‘__invoke’. This method receives the following:

  • A value
  • Name of the attribute
  • A callback

The callback is invoked upon failure with an error message.

<?php
 
namespace App\Rules;
 
use Illuminate\Contracts\Validation\InvokableRule;
 
class Uppercase implements InvokableRule
{
   /**
    * Run the validation rule.
    *
    * @param  string  $attribute
    * @param  mixed  $value
    * @param  \Closure  $fail
    * @return void
    */
   public function __invoke($attribute, $value, $fail)
   {
       if (strtoupper($value) !== $value) {
           $fail('The :attribute must be uppercase.');
       }
   }
}
PHP

By using closures

In the case you only need the functionality of a custom rule only once throughout your application, you can use a closure. The closure receives the following:

  • Values of the attribute
  • $fail callback

The $fail callback is called upon validation failure.

use Illuminate\Support\Facades\Validator;
 
$validator = Validator::make($request->all(), [
   'title' => [
       'required',
       'max:255',
       function ($attribute, $value, $fail) {
           if ($value === 'foo') {
               $fail('The '.$attribute.' is invalid.');
           }
       },
   ],
]);
PHP

Using the implicit rules

When an attribute, being validated, is absent then the Laravel custom validation rule cannot be implemented. Below is an example where the unique custom cannot run in the case of an empty string.

use Illuminate\Support\Facades\Validator;
 
$rules = ['name' => 'unique:users,name'];
 
$input = ['name' => ''];
 
Validator::make($input, $rules)->passes(); // true
PHP

To run a rule when an attribute is empty, ensure that the attribute under consideration is required. To customize an implicit rule, you can implement the ‘Illuminate\Contracts\Validation\ImplicitRule’.

The above is implemented on an interface that serves as a ‘marker interface’ for the validator. This interface does not contain any methods to implement the typical rule interface.

Laravel is widely popular in web development, but its packages can be used to customize the validation rules. This powerful framework also offers tons of built-in functionalities with a set of validators that can be used to validate the request forms.

In some tricky situations, we need a customized validator that can address specific purposes. Laravel enables you to create these custom validators. For better efficiency, remember this:

Implement the Closure and Extend approach present in the validator and use the Rule command to create a ruling class.

Customizing Validation Rules Using Laravel

Author
Jay Galiyal

Jay is a seasoned writer with 7+ years of experience in content marketing. Long-form content, copywriting, and UX writing are his strengths. He also enjoys singing, cooking, and composing poetry.

Share this post