Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

New invokable rule objects in Laravel 9.x

July 19, 2022 ·

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!

Learn the fundamentals of PHP 8 (includes 8.1 and 8.2), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-read guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

👋 Hi there! I'm Amit. I write articles about all things web development. If you like what I write and want me to continue doing the same, I would like you buy me some coffees. I'd highly appreciate that. Cheers!

Comments?