Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

micobarac's avatar

Validator::extend() with custom ValidationRule - Missing Laravel 10 example

In Laravel 10, these validation classes were deprecated:

ImplicitRule
InvokableRule
Rule

I am trying to extend Validator by using Validator::extend() and passing a custom validation rule class, which implements new ValidationRule interface, but I cannot find any example on how to do that.

ValidationRule class has the following method:

public function validate(string $attribute, mixed $value, Closure $fail): void

Validator class has the following method:

public static function extend($rule, $extension, $message = null)

Validator::extend() method's $extension parameter requires a boolean return value, but ValidationRule validate() method returns void. Also, I don't know how instantiate $fail Closure within AppServiceProvider class;

Also, I need additional data and validator instances inside custom validation rule, but I cannot pass them. I found ValidatorAwareRule and DataAwareRule interfaces in Laravel docs, so I have implemented them, but their implemented methods don't trigger at all.

 /**
  * Bootstrap any application services.
  *
  * @return void
  * @throws ReflectionException
  */
 public function boot(): void
 {
     Validator::extend('exists_hashid', ExistsHashid::class);
 }
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Validation\Validator;

class ExistsHashid implements DataAwareRule, ValidationRule, ValidatorAwareRule
{
    public function setData(array $data)
    {
        // This never triggers
    }

    public function setValidator(Validator $validator)
    {
        // This never triggers
    }

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // This fails with "message": "App\Rules\ExistsHashid::validate(): Argument #3 ($fail) must be of type Closure, array given, called in /Users/milan/Sites/tabula/server/vendor/laravel/framework/src/Illuminate/Validation/Validator.php on line 1535"
    }
}

It looks like this is the expected declaration of validate() method:

public function validate(string $attribute, mixed $value, array $data, Validator $validator): bool

which collides with the one implemented from the interface.

How do I put these classes together?

0 likes
10 replies
franciscocaldeira's avatar

Hey, I have this on my current project for the ValidationRule.

<?php
namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class AlphaName implements ValidationRule
{
    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
         $fail('validation.alpha_name')->translate();
    }
}
micobarac's avatar

@franciscocaldeira, how does this suppose to help me? I already posted my validation rule code. Where do you register your validation rule globally by using Validator::extend()?

2 likes
kumiko's avatar

I have the same problem, I have the previous extended rules that implemented Rule, now with ValidationRule and public function validate(string $attribute, mixed $value, \Closure $fail), how is it supposed to be extended?

        Validator::extend('unique_translate', function ($attribute, $value, $parameters, $validator) {
            $rule = new UniqueTranslate(...$parameters);

            return $rule->passes($attribute, $value);
        });
smilkobuta's avatar

@kumiko @micobarac I don't know if it's accurate, but this code worked as expected.

    Validator::extend('unique_translate', function ($attribute, $value, $parameters, $validator) {
        $rule = InvokableValidationRule::make(new UniqueTranslate(...$parameters));
        $rule->setValidator($validator);
        return $rule->passes($attribute, $value);
    });
3 likes
wfroh's avatar

@smilkobuta This code can be wrapped into more generic version:

class RuleExtender {
    public static function extend(string $ruleClass) {
        return function($attribute, $value, $parameters, $validator) use ($ruleClass) {
            $rule = InvokableValidationRule::make(new $ruleClass(...$parameters));
            $rule->setValidator($validator);
            $rule->setData($validator->getData());
            $result = $rule->passes($attribute, $value);
            if (!$result) {
                $validator->customMessages[$attribute] = $rule->message();
            }
            return $result;
        };
    }
}

to be used it like:

Validator::extend('custom_rule', RuleExtender::extend(MyCustomRule::class));
LWlook's avatar

@wfroh With this solution, the message from the fail method is not given to the extend function.

2 likes
Marven's avatar

Would love to find a solution for this as well. For the time being we can keep using the Rule contract. But eventually it will be removed.

2 likes
nmalinoski's avatar

Based on the example provided by @smilkobuta, this mess will pass through the hardcoded fail message in the UniqueTranslate class. Passing that error message, when there is one, should really be implicit, but it isn't.

        Validator::extend('unique_translate', function ($attribute, $value, $parameters, $validator) {
            $rule = InvokableValidationRule::make(new UniqueTranslate)
                ->setValidator($validator);
            $result = $rule->passes($attribute, $value);

            if ($result == false) {
                $validator->setCustomMessages([
                    $attribute => Arr::first($rule->message()),
                ]);
            }

            return $result;
        });
1 like
Reppair's avatar

So, no easy and simple way to Validator::extend('custom_rule', CustomRule::class); or something?

I went down the rabbit hole today looking into the Validator class it self and the InvokableValidation Rule etc. It all seem bit hard to follow and also by the looks of it, there is still more refactoring to be done, towards deprecating the old way.

2 likes

Please or to participate in this conversation.