Jorge Rubira
Jorge Rubira

Reputation: 13

How to use custom Laravel rules with arguments with Lighthouse

I have a doubt about the @rules directive. I have created my own Rule in Laravel but I need to pass an argument in the constructor, so I don't know how I can use the @rules directive properly

@rules(apply: ["App\\Rules\\MyCustomRule"])

How can I do something like this? Because the following line doesn't work

@rules(apply: ["new App\\Rules\\MyCustomRule('arg')"])

Thank you in advance!

Upvotes: 1

Views: 858

Answers (2)

Steve Moretz
Steve Moretz

Reputation: 3138

You should look how other rules work that take arguments, take this for example

exists:users,id

exists rule is getting two arguments, so let's say you want your rule to be

my_custom_rule:arg1,arg2

First create your custom rule using rule objects as you do in Laravel without lighthouse.

https://laravel.com/docs/10.x/validation#using-rule-objects

Here's how yours would go:

  1. Make the class
php artisan make:rule MyCustomRule
  1. Add a constructor to your class, get the arguments and set them as private properties to use in validate method.
class MyCustomRule implements ValidationRule
{
    public function __construct(private string $arg1, private string $arg2)
    {

    }

    /**
     * Run the validation rule.
     *
     * @param  \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString  $fail
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // use $this->arg1, $this->arg2
    }
}
  1. Register your validator in the boot method of a service provider such as AppServiceProvider
private function extend_validator(string $className, string $ruleName){
    Validator::extend($ruleName, static function (string $attribute, mixed $value, array $arguments, \Illuminate\Validation\Validator $validator) use ($ruleName, $className) {
        $implicitRule = InvokableValidationRule::make(new $className(...$arguments))->setValidator($validator);
        if (!($result = $implicitRule->passes($attribute, $value))) {
            // override the message and make it dynamic
            $i = 0;
            $validator->addReplacer($ruleName, function () use ($implicitRule, &$i) {
                return $implicitRule->message()[$i++];
            });
        }
        return $result;
    });
}

public function boot(): void
{
    $this->extend_validator(MyCustomRule::class, "my_custom_rule");
}
  1. Use your validator in lighthouse schema
test(id: String @rules(apply: ["my_custom_rule:arg1,arg2"])) : String

Upvotes: 1

Alex
Alex

Reputation: 1638

It is not possible with @rules(apply: [...]) but this can be achieved by using a validator class.

Upvotes: 1

Related Questions