diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 7c0206b081..36fbda0690 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -3,7 +3,6 @@ Yii Framework 2 Change Log 2.0.7 under development ----------------------- - - Bug #6351: Find MySQL FK constraints from `information_schema` tables instead of `SHOW CREATE TABLE` to improve reliability (nineinchnick) - Bug #6363, #8301, #8582, #9566: Fixed data methods and PJAX issues when used together (derekisbusy) - Bug #6876: Fixed RBAC migration MSSQL cascade problem (thejahweh) @@ -43,6 +42,7 @@ Yii Framework 2 Change Log - Bug #10029: Fixed MaskedInput not working with PJAX (martrix78, samdark) - Bug #10101: Fixed assignments saving on role removing in `\yii\rbac\PhpManager` (rezident1307) - Bug #10142: Fixed `yii\validators\EmailValidator` to check the length of email properly (silverfire) +- Bug #10263: Fixed `yii\validators\UniqueValidator` to work properly when model is not instance of `targetClass` (bupy7, githubjeka, silverfire) - Bug #10278: Fixed `yii\helpers\BaseJson` support \SimpleXMLElement data (SilverFire, LAV45) - Bug #10302: Fixed JS function `yii.getQueryParams`, which parsed array variables incorrectly (servocoder, silverfire) - Bug #10363: Fixed fallback float and integer formatting (AnatolyRugalev, silverfire) diff --git a/framework/validators/UniqueValidator.php b/framework/validators/UniqueValidator.php index 19882c607f..a15b4cb110 100644 --- a/framework/validators/UniqueValidator.php +++ b/framework/validators/UniqueValidator.php @@ -106,8 +106,9 @@ class UniqueValidator extends Validator $query->andWhere($this->filter); } - if (!$model instanceof ActiveRecordInterface || $model->getIsNewRecord()) { + if (!$model instanceof ActiveRecordInterface || $model->getIsNewRecord() || $model->className() !== $targetClass) { // if current $model isn't in the database yet then it's OK just to call exists() + // also there's no need to run check based on primary keys, when $targetClass is not the same as $model's class $exists = $query->exists(); } else { // if current $model is in the database already we can't use exists() diff --git a/tests/data/validators/models/ValidatorTestRefModel.php b/tests/data/validators/models/ValidatorTestRefModel.php index bf0ebe5dbd..bd70734350 100644 --- a/tests/data/validators/models/ValidatorTestRefModel.php +++ b/tests/data/validators/models/ValidatorTestRefModel.php @@ -4,6 +4,11 @@ namespace yiiunit\data\validators\models; use yiiunit\data\ar\ActiveRecord; +/** + * @property int id + * @property string a_field + * @property int ref + */ class ValidatorTestRefModel extends ActiveRecord { diff --git a/tests/framework/validators/UniqueValidatorTest.php b/tests/framework/validators/UniqueValidatorTest.php index 7a1b805725..53990a63f8 100644 --- a/tests/framework/validators/UniqueValidatorTest.php +++ b/tests/framework/validators/UniqueValidatorTest.php @@ -5,8 +5,10 @@ namespace yiiunit\framework\validators; use yii\validators\UniqueValidator; use Yii; use yiiunit\data\ar\ActiveRecord; +use yiiunit\data\ar\Customer; use yiiunit\data\ar\Order; use yiiunit\data\ar\OrderItem; +use yiiunit\data\ar\Profile; use yiiunit\data\validators\models\FakedValidationModel; use yiiunit\data\validators\models\ValidatorTestMainModel; use yiiunit\data\validators\models\ValidatorTestRefModel; @@ -38,6 +40,7 @@ class UniqueValidatorTest extends DatabaseTestCase $m = ValidatorTestMainModel::find()->one(); $val->validateAttribute($m, 'id'); $this->assertFalse($m->hasErrors('id')); + /** @var ValidatorTestRefModel $m */ $m = ValidatorTestRefModel::findOne(1); $val->validateAttribute($m, 'ref'); $this->assertTrue($m->hasErrors('ref')); @@ -73,6 +76,7 @@ class UniqueValidatorTest extends DatabaseTestCase public function testValidateNonDatabaseAttribute() { $val = new UniqueValidator(['targetClass' => ValidatorTestRefModel::className(), 'targetAttribute' => 'ref']); + /** @var ValidatorTestMainModel $m */ $m = ValidatorTestMainModel::findOne(1); $val->validateAttribute($m, 'testMainVal'); $this->assertFalse($m->hasErrors('testMainVal')); @@ -97,6 +101,7 @@ class UniqueValidatorTest extends DatabaseTestCase 'targetAttribute' => ['order_id', 'item_id'], ]); // validate old record + /** @var OrderItem $m */ $m = OrderItem::findOne(['order_id' => 1, 'item_id' => 2]); $val->validateAttribute($m, 'order_id'); $this->assertFalse($m->hasErrors('order_id')); @@ -117,6 +122,7 @@ class UniqueValidatorTest extends DatabaseTestCase 'targetAttribute' => ['id' => 'order_id'], ]); // validate old record + /** @var Order $m */ $m = Order::findOne(1); $val->validateAttribute($m, 'id'); $this->assertTrue($m->hasErrors('id')); @@ -136,4 +142,33 @@ class UniqueValidatorTest extends DatabaseTestCase $val->validateAttribute($m, 'id'); $this->assertFalse($m->hasErrors('id')); } + + public function testValidateTargetClass() + { + // Check whether "Description" and "address" aren't equal + $val = new UniqueValidator([ + 'targetClass' => Customer::className(), + 'targetAttribute' => ['description'=>'address'], + ]); + + /** @var Profile $m */ + $m = Profile::findOne(1); + $this->assertEquals('profile customer 1', $m->description); + $val->validateAttribute($m, 'description'); + $this->assertFalse($m->hasErrors('description')); + + // ID of Profile is not equal to ID of Customer + // (1, description = address2) <=> (2, address = address2) + $m->description = 'address2'; + $val->validateAttribute($m, 'description'); + $this->assertTrue($m->hasErrors('description')); + $m->clearErrors('description'); + + // ID of Profile IS equal to ID of Customer + // (1, description = address1) <=> (1, address = address1) + // https://github.com/yiisoft/yii2/issues/10263 + $m->description = 'address1'; + $val->validateAttribute($m, 'description'); + $this->assertTrue($m->hasErrors('description')); + } }