mirror of
https://github.com/yiisoft/yii2.git
synced 2025-11-28 04:59:03 +08:00
Fixes #797: Added support for validating multiple columns by UniqueValidator and ExistValidator
This commit is contained in:
@@ -16,6 +16,7 @@ Yii Framework 2 Change Log
|
||||
- Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark)
|
||||
- Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe)
|
||||
- Bug: Fixed issue with tabular input on ActiveField::radio() and ActiveField::checkbox() (jom)
|
||||
- Enh #797: Added support for validating multiple columns by `UniqueValidator` and `ExistValidator` (qiangxue)
|
||||
- Enh #1293: Replaced Console::showProgress() with a better approach. See Console::startProgress() for details (cebe)
|
||||
- Enh #1406: DB Schema support for Oracle Database (p0larbeer, qiangxue)
|
||||
- Enh #1437: Added ListView::viewParams (qiangxue)
|
||||
|
||||
@@ -30,10 +30,25 @@ class ExistValidator extends Validator
|
||||
*/
|
||||
public $className;
|
||||
/**
|
||||
* @var string the yii\db\ActiveRecord class attribute name that should be
|
||||
* @var string|array the ActiveRecord class attribute name that should be
|
||||
* used to look for the attribute value being validated. Defaults to null,
|
||||
* meaning using the name of the attribute being validated.
|
||||
* @see className
|
||||
* meaning using the name of the attribute being validated. Use a string
|
||||
* to specify the attribute that is different from the attribute being validated
|
||||
* (often used together with [[className]]). Use an array to validate the existence about
|
||||
* multiple columns. For example,
|
||||
*
|
||||
* ```php
|
||||
* // a1 needs to exist
|
||||
* array('a1', 'exist')
|
||||
* // a1 needs to exist, but its value will use a2 to check for the existence
|
||||
* array('a1', 'exist', 'attributeName' => 'a2')
|
||||
* // a1 and a2 need to exist together, and they both will receive error message
|
||||
* array('a1, a2', 'exist', 'attributeName' => array('a1', 'a2'))
|
||||
* // a1 and a2 need to exist together, only a1 will receive error message
|
||||
* array('a1', 'exist', 'attributeName' => array('a1', 'a2'))
|
||||
* // a1 and a2 need to exist together, a2 will take value 10, only a1 will receive error message
|
||||
* array('a1', 'exist', 'attributeName' => array('a1', 'a2' => 10))
|
||||
* ```
|
||||
*/
|
||||
public $attributeName;
|
||||
|
||||
@@ -64,9 +79,7 @@ class ExistValidator extends Validator
|
||||
/** @var \yii\db\ActiveRecordInterface $className */
|
||||
$className = $this->className === null ? get_class($object) : $this->className;
|
||||
$attributeName = $this->attributeName === null ? $attribute : $this->attributeName;
|
||||
$query = $className::find();
|
||||
$query->where([$attributeName => $value]);
|
||||
if (!$query->exists()) {
|
||||
if (!$this->exists($className, $attributeName, $object, $value)) {
|
||||
$this->addError($object, $attribute, $this->message);
|
||||
}
|
||||
}
|
||||
@@ -85,10 +98,33 @@ class ExistValidator extends Validator
|
||||
if ($this->attributeName === null) {
|
||||
throw new InvalidConfigException('The "attributeName" property must be set.');
|
||||
}
|
||||
return $this->exists($this->className, $this->attributeName, null, $value) ? null : [$this->message, []];
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs existence check.
|
||||
* @param string $className the AR class name to be checked against
|
||||
* @param string|array $attributeName the attribute(s) to be checked
|
||||
* @param \yii\db\ActiveRecordInterface $object the object whose value is being validated
|
||||
* @param mixed $value the attribute value currently being validated
|
||||
* @return boolean whether the data being validated exists in the database already
|
||||
*/
|
||||
protected function exists($className, $attributeName, $object, $value)
|
||||
{
|
||||
/** @var \yii\db\ActiveRecordInterface $className */
|
||||
$className = $this->className;
|
||||
$query = $className::find();
|
||||
$query->where([$this->attributeName => $value]);
|
||||
return $query->exists() ? null : [$this->message, []];
|
||||
if (is_array($attributeName)) {
|
||||
$params = [];
|
||||
foreach ($attributeName as $k => $v) {
|
||||
if (is_integer($k)) {
|
||||
$params[$v] = $this->className === null && $object !== null ? $object->$v : $value;
|
||||
} else {
|
||||
$params[$k] = $v;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$params = [$attributeName => $value];
|
||||
}
|
||||
return $query->where($params)->exists();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,9 +26,25 @@ class UniqueValidator extends Validator
|
||||
*/
|
||||
public $className;
|
||||
/**
|
||||
* @var string the ActiveRecord class attribute name that should be
|
||||
* @var string|array the ActiveRecord class attribute name that should be
|
||||
* used to look for the attribute value being validated. Defaults to null,
|
||||
* meaning using the name of the attribute being validated.
|
||||
* meaning using the name of the attribute being validated. Use a string
|
||||
* to specify the attribute that is different from the attribute being validated
|
||||
* (often used together with [[className]]). Use an array to validate uniqueness about
|
||||
* multiple columns. For example,
|
||||
*
|
||||
* ```php
|
||||
* // a1 needs to be unique
|
||||
* array('a1', 'unique')
|
||||
* // a1 needs to be unique, but its value will use a2 to check for the uniqueness
|
||||
* array('a1', 'unique', 'attributeName' => 'a2')
|
||||
* // a1 and a2 need to unique together, and they both will receive error message
|
||||
* array('a1, a2', 'unique', 'attributeName' => array('a1', 'a2'))
|
||||
* // a1 and a2 need to unique together, only a1 will receive error message
|
||||
* array('a1', 'unique', 'attributeName' => array('a1', 'a2'))
|
||||
* // a1 and a2 need to unique together, a2 will take value 10, only a1 will receive error message
|
||||
* array('a1', 'unique', 'attributeName' => array('a1', 'a2' => 10))
|
||||
* ```
|
||||
*/
|
||||
public $attributeName;
|
||||
|
||||
@@ -60,7 +76,20 @@ class UniqueValidator extends Validator
|
||||
$attributeName = $this->attributeName === null ? $attribute : $this->attributeName;
|
||||
|
||||
$query = $className::find();
|
||||
$query->where([$attributeName => $value]);
|
||||
|
||||
if (is_array($attributeName)) {
|
||||
$params = [];
|
||||
foreach ($attributeName as $k => $v) {
|
||||
if (is_integer($k)) {
|
||||
$params[$v] = $this->className === null ? $object->$v : $value;
|
||||
} else {
|
||||
$params[$k] = $v;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$params = [$attributeName => $value];
|
||||
}
|
||||
$query->where($params);
|
||||
|
||||
if (!$object instanceof ActiveRecordInterface || $object->getIsNewRecord()) {
|
||||
// if current $object isn't in the database yet then it's OK just to call exists()
|
||||
@@ -71,7 +100,11 @@ class UniqueValidator extends Validator
|
||||
$objects = $query->limit(2)->all();
|
||||
$n = count($objects);
|
||||
if ($n === 1) {
|
||||
if (in_array($attributeName, $className::primaryKey())) {
|
||||
$keys = array_keys($params);
|
||||
$pks = $className::primaryKey();
|
||||
sort($keys);
|
||||
sort($pks);
|
||||
if ($keys === $pks) {
|
||||
// primary key is modified and not unique
|
||||
$exists = $object->getOldPrimaryKey() != $object->getPrimaryKey();
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user