New invokable rule objects in Laravel 9.x
Validating incoming HTTP requests is an integral part of any robust web application and if you work with Laravel, it makes it a breeze to work with validation.
It even lets you set up your own validation rules in form of “rule objects”. Essentially rule objects are custom classes using which one can define their own set of validation rules.
Traditional approach
So, if you’ve worked with any Laravel version before 9.x, you may have seen a rule object that can be generated using a convenient make:rule
command. So, for instance, if we want to set up a TitleCase
rule object, we can generate it using the php artisan make:rule Titlecase
command like so.
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class Titlecase implements Rule
{
// commented constructor for brevity
/**
* Determine if the validation rule passes.
*
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
if (ucwords($value) !== $value) {
$message[] = 'Each word in :attribute must begin with a capital letter';
return false;
}
return $this->messages === [];
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return $message;
}
}
As you can tell, inside this rule object, the validation rules can be built in the passes
method and the error messages (if there are any) can be returned from the message
method.
This works well but there are a few issues. The class as you can see can become quite bulky since you have to do return false
every time a validation rule fails. Also, there’s one more method message
that we need to maintain just to return an error message.
To simplify this, a PR by Tim MacDonald in Laravel 9.x introduces a new way of defining these rule objects and that’s using invokable classes!
Invokable rule objects
As I said, Laravel 9.x introduces a new invokable class-based validation implementation that would attempt to simplify the traditional class-based rule objects.
Right out of the bat, if you want to generate invokable classes based rule objects, you can use the same make:rule
Artisan command with the --invokable
option like so.
$ php artisan make:rule Titlecase --invokable
This will generate a Titlecase
invokable rule object under the app/Rules
directory like so.
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\InvokableRule;
class Titlecase implements InvokableRule
{
/**
* Run the validation rule.
*
* @param string $attribute
* @param mixed $value
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
* @return void
*/
public function __invoke($attribute, $value, $fail)
{
//
}
}
As you can tell, the rule object class only has one method to it. And that is __invoke
. The attributes are the same as the passes
method with an additional one called $fail
which is a callback that should be invoked on failure with the validation error message. This makes this implementation pretty simple to work with.
So, if we want to rewrite our previous TitleCase
rule object with this approach, here’s what that would look like.
public function __invoke($attribute, $value, $fail)
{
if (ucwords($value) !== $value) {
$fail('Each word in :attribute must begin with a capital letter');
}
}
That’s it! That’s all we need to do. It’s as simple as it gets.
No more returning false
after failing cases nor maintaining a separate method to return error messages.
On top of this, it also brings first-party translation support to failure messages, without the need to reach for the translation helper.
if (ucwords($value) !== $value) {
$fail('validation.post_title.no_titlecase')->translate();
}
And that’s all about the new invokable validation rules in Laravel!
Like this article?
Buy me a coffee👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.