mirror of
https://github.com/yiisoft/yii2.git
synced 2025-08-26 14:26:54 +08:00
Fix #19407: Fix yii\validators\UniqueValidator
and yii\validators\ExistValidator
to respect skipOnError
option for target attributes
This commit is contained in:
@ -30,6 +30,7 @@ Yii Framework 2 Change Log
|
||||
- Bug #19368: Fix PHP 8.1 error when `$fileMimeType` is `null` in `yii\validators\FileValidator::validateMimeType()` (bizley)
|
||||
- Enh #19384: Normalize `setBodyParams()` and `getBodyParam()` in `yii\web\Request` (WinterSilence, albertborsos)
|
||||
- Bug #19386: Fix recursive calling `yii\helpers\BaseArrayHelper::htmlDecode()` (WinterSilence)
|
||||
- Bug #19407: Fix `yii\validators\UniqueValidator` and `yii\validators\ExistValidator` to respect `skipOnError` option for target attributes (bizley)
|
||||
- Bug #19418: Fix `yii\filters\auth\CompositeAuth` ignoring `only` and `except` options (lesha724)
|
||||
- Enh #19401: Delay `exit(1)` in `yii\base\ErrorHandler::handleFatalError` (arrilot)
|
||||
- Bug #19402: Add shutdown event and fix working directory in `yii\base\ErrorHandler` (WinterSilence)
|
||||
|
@ -153,6 +153,14 @@ class ExistValidator extends Validator
|
||||
private function checkTargetAttributeExistence($model, $attribute)
|
||||
{
|
||||
$targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
|
||||
if ($this->skipOnError) {
|
||||
foreach ((array)$targetAttribute as $k => $v) {
|
||||
if ($model->hasErrors(is_int($k) ? $v : $k)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$params = $this->prepareConditions($targetAttribute, $model, $attribute);
|
||||
$conditions = [$this->targetAttributeJunction == 'or' ? 'or' : 'and'];
|
||||
|
||||
|
@ -122,9 +122,15 @@ class UniqueValidator extends Validator
|
||||
*/
|
||||
public function validateAttribute($model, $attribute)
|
||||
{
|
||||
/* @var $targetClass ActiveRecordInterface */
|
||||
$targetClass = $this->getTargetClass($model);
|
||||
$targetAttribute = $this->targetAttribute === null ? $attribute : $this->targetAttribute;
|
||||
if ($this->skipOnError) {
|
||||
foreach ((array)$targetAttribute as $k => $v) {
|
||||
if ($model->hasErrors(is_int($k) ? $v : $k)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rawConditions = $this->prepareConditions($targetAttribute, $model, $attribute);
|
||||
$conditions = [$this->targetAttributeJunction === 'or' ? 'or' : 'and'];
|
||||
|
||||
@ -136,6 +142,8 @@ class UniqueValidator extends Validator
|
||||
$conditions[] = [$key => $value];
|
||||
}
|
||||
|
||||
/* @var $targetClass ActiveRecordInterface */
|
||||
$targetClass = $this->getTargetClass($model);
|
||||
$db = $targetClass::getDb();
|
||||
|
||||
$modelExists = false;
|
||||
|
@ -10,6 +10,7 @@ namespace yiiunit\framework\validators;
|
||||
use yii\base\Exception;
|
||||
use yii\validators\ExistValidator;
|
||||
use yiiunit\data\ar\ActiveRecord;
|
||||
use yiiunit\data\ar\Customer;
|
||||
use yiiunit\data\ar\Order;
|
||||
use yiiunit\data\ar\OrderItem;
|
||||
use yiiunit\data\validators\models\ValidatorTestMainModel;
|
||||
@ -268,4 +269,31 @@ abstract class ExistValidatorTest extends DatabaseTestCase
|
||||
|
||||
ActiveRecord::$db = $this->getConnection();
|
||||
}
|
||||
|
||||
public function testSecondTargetAttributeWithError()
|
||||
{
|
||||
$validator = new ExistValidator(['targetAttribute' => ['email', 'name']]);
|
||||
$customer = new Customer();
|
||||
$customer->email = 'user11111@example.com';
|
||||
$customer->name = 'user11111';
|
||||
|
||||
$validator->validateAttribute($customer, 'email');
|
||||
$this->assertTrue($customer->hasErrors('email'));
|
||||
|
||||
$customer->clearErrors();
|
||||
|
||||
$customer->addError('name', 'error');
|
||||
$validator->validateAttribute($customer, 'email');
|
||||
$this->assertFalse($customer->hasErrors('email')); // validator should be skipped
|
||||
|
||||
$validator = new ExistValidator([
|
||||
'targetAttribute' => ['email', 'name'],
|
||||
'skipOnError' => false,
|
||||
]);
|
||||
|
||||
$customer->clearErrors();
|
||||
$customer->addError('name', 'error');
|
||||
$validator->validateAttribute($customer, 'email');
|
||||
$this->assertTrue($customer->hasErrors('email')); // validator should not be skipped
|
||||
}
|
||||
}
|
||||
|
@ -505,6 +505,33 @@ abstract class UniqueValidatorTest extends DatabaseTestCase
|
||||
|
||||
ActiveRecord::$db = $this->getConnection();
|
||||
}
|
||||
|
||||
public function testSecondTargetAttributeWithError()
|
||||
{
|
||||
$validator = new UniqueValidator(['targetAttribute' => ['email', 'name']]);
|
||||
$customer = new Customer();
|
||||
$customer->email = 'user1@example.com';
|
||||
$customer->name = 'user1';
|
||||
|
||||
$validator->validateAttribute($customer, 'email');
|
||||
$this->assertTrue($customer->hasErrors('email'));
|
||||
|
||||
$customer->clearErrors();
|
||||
|
||||
$customer->addError('name', 'error');
|
||||
$validator->validateAttribute($customer, 'email');
|
||||
$this->assertFalse($customer->hasErrors('email')); // validator should be skipped
|
||||
|
||||
$validator = new UniqueValidator([
|
||||
'targetAttribute' => ['email', 'name'],
|
||||
'skipOnError' => false,
|
||||
]);
|
||||
|
||||
$customer->clearErrors();
|
||||
$customer->addError('name', 'error');
|
||||
$validator->validateAttribute($customer, 'email');
|
||||
$this->assertTrue($customer->hasErrors('email')); // validator should not be skipped
|
||||
}
|
||||
}
|
||||
|
||||
class WithCustomer extends Customer {
|
||||
|
Reference in New Issue
Block a user