For Developers

Customizing Validation Rules Using Laravel

Customizing Validation Rules Using Laravel

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']);

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...
   }
}

Code source

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...
}

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'],
]);

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',
]);

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',
]);

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

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',
   ];
}

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']);

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!');
       }
   });
}

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;

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';

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);
}

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;
}

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),
   ]);
}

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',
]);

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',
]);

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';
});

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)],
]);

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()

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

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

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);

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

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.');
       }
   }
}

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
  • Name 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.');
           }
       },
   ],
]);

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

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.

Author

  • 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.

Frequently Asked Questions

Creating a custom validation rule in Laravel is simple. Just follow these steps:

  1. Create a new controller

  2. Build the routes for your new custom validation rules

  3. Generate a new request

  4. Build new controller methods

  5. Creating the views

  6. Adding new customized validation rules

You can customize the validation error messages by changing the array of attributes with the custom error messages in the ‘messages()’ method. You need to change the array of attributes.

The form request class offers you two different methods including rules() and auth(). According to your requirements you can write your customized rules with the rules()n method and with the auth() method, you can perform any authorization logic.

The ‘withErrors’ method. You can use this method to flash the error messages to the ongoing session. The $errors variable is automatically shared with the views post redirection using this method. This enables you to redirect them back to the users.

View more FAQs
Press

Press

What's up with Turing? Get the latest news about us here.
Blog

Blog

Know more about remote work.
Checkout our blog here.
Contact

Contact

Have any questions?
We'd love to hear from you.

Hire and manage remote developers

Tell us the skills you need and we'll find the best developer for you in days, not weeks.

Hire Developers