Philipp Mochine
Philipp Mochine

Reputation: 4705

How to pass parameters in Laravel Rule?

I would like to move this code to the App\Rule:

//Currently in class AppServiceProvider extends ServiceProvider

Validator::extend('empty_if', function ($attribute, $value, $parameters, $validator) {
   return ($value != '' && request($parameters[0]) != '') ? false : true;
});

So that it should be this:

//in: App\Rules\EmptyIf

public function passes($attribute, $value, $parameters)
{
   return ($value != '' && request($parameters[0]) != '') ? false : true;
}

But my problem is, that I cannot pass $parameters with

Validator::extend('empty_if', 'App\Rules\EmptyIf@passes');

How would you pass parameters to Laravel Rule?

Upvotes: 14

Views: 30040

Answers (5)

Lilit Aghayan
Lilit Aghayan

Reputation: 79

You can pass parameters with constructor in Rule

public function __construct($params)
{
    $this->params = $params;
}

Then access parameters in passes method

public function passes($attribute, $value)
{
  //access params with $this->params
}

Upvotes: 6

Nathan Brown
Nathan Brown

Reputation: 257

Here is a simpler and updated way of accomplishing this, without extending the validator. You can access the passed param in the Rule's constructor, so just set a globally scoped variable and now you can reference it inside of the passes() method. You could even use the same approach to have the value in the validator message.

The validate call:

case 'measurement':
 $request->validate([
   'updates.*.value' => [
   new Measurement('foo-bar'),
   ],
 ]);
 break;

The Rule:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class Measurement implements Rule
{
/**
 * Create a new rule instance.
 *
 * @param $param
 */
public function __construct($param)
{
    $this->type = $param;
}

public $type;
/**
 * Determine if the validation rule passes.
 *
 * @param string $attribute
 * @param mixed $value
 * @param array $parameters
 * @return bool
 */
public function passes($attribute, $value)
{
    dd($this->type, 'params');
    return;
}

Upvotes: 22

HorusKol
HorusKol

Reputation: 8706

You can do this by passing the parameter(s) into the constructor for your Rule:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;
use Illuminate\Http\Request;

class EmptyIf implements Rule
{
    public $otherParameter;

    public function passes($attribute, $value)
    {
        if (Request::get($this->otherParameter) != '') {
            return ($value == '');
        }

        return true;
    }

    public function message()
    {
        return 'The :attribute cannot be empty if ' . $this->otherParameter . ' is set';
    }
}

And in the service provider:

<?php

namespace App\Providers;

use App\Rules\EmptyIf;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap services.
     */
    public function boot(): void
    {
        Validator::extend('empty_if', function($attribute, $value, $parameters, $validator) {
            $rule = new EmptyIf($parameters[0]);

            return $rule->passes();
        });
    }
}

So now you can use the string form in your validator:

'sometimes|nullable|empty_if:about_image|max:200|url'

Upvotes: 3

cweiske
cweiske

Reputation: 31088

When using a rule class with required $parameters, you'll get an error:

PHP Fatal error:
Declaration of App\Rules\AgeAt::passes($attribute, $value, $parameters)
must be compatible with Illuminate\Contracts\Validation\Rule::passes($attribute, $value)

You can simply make that $parameters optional, and you will get the parameters passed in Laravel 5.5+ automatically:

public function passes($attribute, $value, $parameters = [])
{
    //do something here
}

Upvotes: 6

apokryfos
apokryfos

Reputation: 40683

If I understand what you need correctly you don't need to extend the validator.

You seem to have class:

class EmptyIf extends Rule {
      public function passes($attribute, $value, $parameters) { }
}

Then you can just use this as:

$this->validate($data, [ "entry" => [ new EmptyIf() ] ]);

You might be able to do both using:

Validator::extend('empty_if', function ($attribute, $value, $parameters) {
    return (new EmptyIf())->passes($atribute, $value, $parameters);
 });

Upvotes: 6

Related Questions