diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md index 0882930915..6592608453 100644 --- a/framework/CHANGELOG.md +++ b/framework/CHANGELOG.md @@ -17,6 +17,7 @@ Yii Framework 2 Change Log - Enh #14081: Added `yii\caching\CacheInterface` to make custom cache extensions adoption easier (silverfire) - Chg #14286: Used primary inputmask package name instead of an alias (samdark) - Enh #14298: The default response formatter configs defined by `yii\web\Response::defaultFormatters()` now use the array syntax (brandonkelly) +- Bug #14304: Fixed `yii\validators\UniqueValidator` and `yii\validators\ExistValidator` to skip prefixes in case expressions are used (samdark) - Bug #14341: Fixed regression in error handling introduced by fixing #14264 (samdark) 2.0.12 June 05, 2017 diff --git a/framework/validators/ExistValidator.php b/framework/validators/ExistValidator.php index 91b78b33dc..a582701a46 100644 --- a/framework/validators/ExistValidator.php +++ b/framework/validators/ExistValidator.php @@ -221,10 +221,16 @@ class ExistValidator extends Validator } $prefixedConditions = []; foreach ($conditions as $columnName => $columnValue) { - $prefixedColumn = "{$alias}.[[" . preg_replace( + if (strpos($columnName, '(') === false) { + $prefixedColumn = "{$alias}.[[" . preg_replace( '/^' . preg_quote($alias) . '\.(.*)$/', '$1', $columnName) . ']]'; + } else { + // there is an expression, can't prefix it reliably + $prefixedColumn = $columnName; + } + $prefixedConditions[$prefixedColumn] = $columnValue; } return $prefixedConditions; diff --git a/framework/validators/UniqueValidator.php b/framework/validators/UniqueValidator.php index d4d1b61ba7..5b569a279c 100644 --- a/framework/validators/UniqueValidator.php +++ b/framework/validators/UniqueValidator.php @@ -288,10 +288,16 @@ class UniqueValidator extends Validator } $prefixedConditions = []; foreach ($conditions as $columnName => $columnValue) { - $prefixedColumn = "{$alias}.[[" . preg_replace( + if (strpos($columnName, '(') === false) { + $prefixedColumn = "{$alias}.[[" . preg_replace( '/^' . preg_quote($alias) . '\.(.*)$/', '$1', $columnName) . ']]'; + } else { + // there is an expression, can't prefix it reliably + $prefixedColumn = $columnName; + } + $prefixedConditions[$prefixedColumn] = $columnValue; } return $prefixedConditions; diff --git a/tests/framework/validators/ExistValidatorTest.php b/tests/framework/validators/ExistValidatorTest.php index 385c723d3f..b91ab41808 100644 --- a/tests/framework/validators/ExistValidatorTest.php +++ b/tests/framework/validators/ExistValidatorTest.php @@ -11,6 +11,7 @@ use Yii; use yii\base\Exception; use yii\validators\ExistValidator; use yiiunit\data\ar\ActiveRecord; +use yiiunit\data\ar\Document; use yiiunit\data\ar\Order; use yiiunit\data\ar\OrderItem; use yiiunit\data\validators\models\ValidatorTestMainModel; @@ -190,4 +191,20 @@ abstract class ExistValidatorTest extends DatabaseTestCase OrderItem::$tableName = $oldTableName; } + + /** + * Test expresssion in targetAttribute + * @see https://github.com/yiisoft/yii2/issues/14304 + */ + public function testExpresionInAttributeColumnName() + { + $val = new ExistValidator([ + 'targetClass' => OrderItem::className(), + 'targetAttribute' => ['id' => 'COALESCE(order_id, 0)'], + ]); + + $m = new Order(['id' => 1]); + $val->validateAttribute($m, 'id'); + $this->assertFalse($m->hasErrors('id')); + } } diff --git a/tests/framework/validators/UniqueValidatorTest.php b/tests/framework/validators/UniqueValidatorTest.php index 2ceb40b32a..84e2af5f25 100644 --- a/tests/framework/validators/UniqueValidatorTest.php +++ b/tests/framework/validators/UniqueValidatorTest.php @@ -11,6 +11,7 @@ use Yii; use yii\validators\UniqueValidator; use yiiunit\data\ar\ActiveRecord; use yiiunit\data\ar\Customer; +use yiiunit\data\ar\Document; use yiiunit\data\ar\Order; use yiiunit\data\ar\OrderItem; use yiiunit\data\ar\Profile; @@ -412,4 +413,25 @@ abstract class UniqueValidatorTest extends DatabaseTestCase $validator->validateAttribute($model, 'id'); $this->assertFalse($model->hasErrors()); } + + /** + * Test expresssion in targetAttribute + * @see https://github.com/yiisoft/yii2/issues/14304 + */ + public function testExpresionInAttributeColumnName() + { + $validator = new UniqueValidator([ + 'targetAttribute' => [ + 'title' => 'LOWER(title)', + ], + ]); + $model = new Document(); + $model->id = 42; + $model->title = 'Test'; + $model->content = 'test'; + $model->version = 1; + $model->save(false); + $validator->validateAttribute($model, 'title'); + $this->assertFalse($model->hasErrors(), 'There were errors: ' . json_encode($model->getErrors())); + } }