Added yii\validators\EachValidator::$stopOnFirstError allowing addition of more than one error

This commit is contained in:
Klimov Paul
2017-01-13 17:59:10 +02:00
parent b93856cdfd
commit d4ac47abe7
3 changed files with 47 additions and 10 deletions

View File

@ -88,6 +88,7 @@ Yii Framework 2 Change Log
- Enh #13074: Improved `yii\log\SyslogTarget` with `$options` to be able to change the default `openlog` options (timbeks)
- Enh #13122: Optimized query for information about foreign keys in `yii\db\oci` (zlakomanoff)
- Enh #13202: Refactor validateAttribute method in UniqueValidator (developeruz)
- Enh #13266: Added `yii\validators\EachValidator::$stopOnFirstError` allowing addition of more than one error (klimov-paul)
- Enh #13264: Added `yii\widgets\InputWidget::$field` field, allowing access to the related `yii\widget\ActiveField` instance (klimov-paul)
- Enh #13268: Added logging of memory usage (bashkarev)
- Enh: Added constants for specifying `yii\validators\CompareValidator::$type` (cebe)

View File

@ -60,6 +60,15 @@ class EachValidator extends Validator
* If disabled, own error message value will be used always.
*/
public $allowMessageFromRule = true;
/**
* @var bool whether to stop validation once first error among attribute value elements is detected.
* When enabled validation will produce single error message on attribute, when disabled - multiple
* error messages mya appear: one per each invalid value.
* Note that this option will affect only [[validateAttribute()]] value, while [[validateValue()]] will
* not be affected.
* @since 2.0.11
*/
public $stopOnFirstError = true;
/**
* @var Validator validator instance.
@ -125,30 +134,35 @@ class EachValidator extends Validator
$validator = $this->getValidator($model); // ensure model context while validator creation
$originalErrors = $model->getErrors($attribute);
$filteredValue = [];
$detectedErrors = $model->getErrors($attribute);
$filteredValue = $model->$attribute;
foreach ($value as $k => $v) {
$model->clearErrors($attribute);
$model->$attribute = $v;
if (!$validator->skipOnEmpty || !$validator->isEmpty($v)) {
$validator->validateAttribute($model, $attribute);
}
$filteredValue[$k] = $model->$attribute;
if ($model->hasErrors($attribute)) {
$validationErrors = $model->getErrors($attribute);
$model->clearErrors($attribute);
if (!empty($originalErrors)) {
$model->addErrors([$attribute => $originalErrors]);
}
if ($this->allowMessageFromRule) {
$model->addErrors([$attribute => $validationErrors]);
$validationErrors = $model->getErrors($attribute);
$detectedErrors = array_merge($detectedErrors, $validationErrors);
} else {
$model->clearErrors($attribute);
$this->addError($model, $attribute, $this->message, ['value' => $v]);
$detectedErrors[] = $model->getFirstError($attribute);
}
$model->$attribute = $value;
return;
if ($this->stopOnFirstError) {
break;
}
}
}
$model->$attribute = $filteredValue;
$model->clearErrors($attribute);
$model->addErrors([$attribute => $detectedErrors]);
}
/**

View File

@ -155,4 +155,26 @@ class EachValidatorTest extends TestCase
$validator->validateAttribute($model, 'attr_one');
$this->assertEmpty($model->getErrors('attr_one'));
}
/**
* @depends testValidate
*/
public function testStopOnFirstError()
{
$model = FakedValidationModel::createWithAttributes([
'attr_one' => [
'one', 2, 'three'
],
]);
$validator = new EachValidator(['rule' => ['integer']]);
$validator->stopOnFirstError = true;
$validator->validateAttribute($model, 'attr_one');
$this->assertCount(1, $model->getErrors('attr_one'));
$model->clearErrors();
$validator->stopOnFirstError = false;
$validator->validateAttribute($model, 'attr_one');
$this->assertCount(2, $model->getErrors('attr_one'));
}
}