1

is there a way to use the when attribute in nested EachValidator? Here is my rule but it does not work:

[['list'], 'each', 'rule' => ['required', 'when' => function ($model) {return false;}, 'whenClient' => "function (attribute, value) {return false;}"]],

I want to test if I can avoid the required validation on some condition. So to test it I always say return false. It is just a testing return statement to verify if it is working.

Muhammad Omer Aslam
  • 22,976
  • 9
  • 42
  • 68
EvilKarter
  • 267
  • 7
  • 22
  • can you please explain a bit more that what you are **actually** trying to do , what you said **I want to test if I can avoid the required validation on some condition.** but that does not define the actual problem may be the approach to the problem should be different than the current one. – Muhammad Omer Aslam Feb 20 '19 at 22:02

1 Answers1

0

If you take a look at yii\validators\EachValidator source, when attribute from validator inside rule never got called:

if (!$validator->skipOnEmpty || !$validator->isEmpty($v)) {
    $validator->validateAttribute($model, $attribute);
}

Compare that to its parent class yii\validators\Validator, it has this:

foreach ($attributes as $attribute) {
    $skip = $this->skipOnError && $model->hasErrors($attribute)
        || $this->skipOnEmpty && $this->isEmpty($model->$attribute);
    if (!$skip) {
        if ($this->when === null || call_user_func($this->when, $model, $attribute)) {
            $this->validateAttribute($model, $attribute);
        }
    }
}

One way to achieve your goal is to extend yii\validators\EachValidator and override its validateAttribute($model, $attribute) method (as well as private property and methods defined there):

/**
 * @var Validator validator instance.
 */
private $_validator;

/**
 * Returns the validator declared in [[rule]].
 * @param Model|null $model model in which context validator should be created.
 * @return Validator the declared validator.
 */
private function getValidator($model = null)
{
    // same as in parent
}

/**
 * Creates validator object based on the validation rule specified in [[rule]].
 * @param Model|null $model model in which context validator should be created.
 * @throws \yii\base\InvalidConfigException
 * @return Validator validator instance
 */
private function createEmbeddedValidator($model)
{
    // same as in parent
}

/**
 * {@inheritdoc}
 */
public function validateAttribute($model, $attribute)
{
    ... // same as in parent
    foreach ($value as $k => $v) {
        $model->clearErrors($attribute);
        $model->$attribute = $v;
        // start override original code
        $skip = $validator->skipOnEmpty && $validator->isEmpty($v);
        if (!$skip) {
            if ($validator->when === null || call_user_func($validator->when, $model, $attribute)) {
                $validator->validateAttribute($model, $attribute);
            }
        }
        // end override original code
        $filteredValue[$k] = $model->$attribute;
        if ($model->hasErrors($attribute)) {
            ... // same as in parent
        }
    }
    ... // same as in parent
}

Use your custom class in your rules() like this:

[['list'], \app\models\EachValidator::className(), 'rule' => ['required', 'when' => function ($model) {return false;}]]
Khotim
  • 81
  • 1
  • 2