Fixes #14254: add an option to specify whether validator is forced to always use master DB for yii\validators\UniqueValidator and yii\validators\ExistValidator

This commit is contained in:
rossoneri
2018-02-15 07:12:54 +08:00
committed by Alexander Makarov
parent 41cf14e515
commit 63ffae028e
6 changed files with 164 additions and 18 deletions

View File

@ -12,6 +12,7 @@ use yii\base\InvalidConfigException;
use yii\base\Model;
use yii\db\ActiveQuery;
use yii\db\ActiveRecord;
use yii\db\QueryInterface;
/**
* ExistValidator validates that the attribute value exists in a table.
@ -79,6 +80,12 @@ class ExistValidator extends Validator
*/
public $targetAttributeJunction = 'and';
/**
* @var bool whether this validator is forced to always use master DB
* @since 2.0.14
*/
public $forceMasterDb = true;
/**
* {@inheritdoc}
@ -105,12 +112,25 @@ class ExistValidator extends Validator
/**
* Validates existence of the current attribute based on relation name
* @param \yii\base\Model $model the data model to be validated
* @param \yii\db\ActiveRecord $model the data model to be validated
* @param string $attribute the name of the attribute to be validated.
*/
private function checkTargetRelationExistence($model, $attribute)
{
if (!$model->{'get' . ucfirst($this->targetRelation)}()->exists()) {
$exists = false;
/** @var ActiveQuery $relationQuery */
$relationQuery = $model->{'get' . ucfirst($this->targetRelation)}();
if ($this->forceMasterDb) {
$model::getDb()->useMaster(function() use ($relationQuery, &$exists) {
$exists = $relationQuery->exists();
});
} else {
$relationQuery->exists();
}
if (!$exists) {
$this->addError($model, $attribute, $this->message);
}
}
@ -142,11 +162,7 @@ class ExistValidator extends Validator
$targetClass = $this->targetClass === null ? get_class($model) : $this->targetClass;
$query = $this->createQuery($targetClass, $conditions);
if (is_array($model->$attribute)) {
if ($query->count("DISTINCT [[$targetAttribute]]") != count($model->$attribute)) {
$this->addError($model, $attribute, $this->message);
}
} elseif (!$query->exists()) {
if (!$this->valueExists($targetClass, $query, $model->$attribute)) {
$this->addError($model, $attribute, $this->message);
}
}
@ -210,17 +226,53 @@ class ExistValidator extends Validator
throw new InvalidConfigException('The "targetAttribute" property must be configured as a string.');
}
$query = $this->createQuery($this->targetClass, [$this->targetAttribute => $value]);
if (is_array($value)) {
if (!$this->allowArray) {
return [$this->message, []];
}
return $query->count("DISTINCT [[$this->targetAttribute]]") == count($value) ? null : [$this->message, []];
if (is_array($value) && !$this->allowArray) {
return [$this->message, []];
}
return $query->exists() ? null : [$this->message, []];
$query = $this->createQuery($this->targetClass, [$this->targetAttribute => $value]);
return $this->valueExists($this->targetClass, $query, $value) ? null : [$this->message, []];
}
/**
* Check whether value exists in target table
*
* @param string $targetClass
* @param QueryInterface $query
* @param mixed $value the value want to be checked
* @return boolean
*/
private function valueExists($targetClass, $query, $value)
{
$db = $targetClass::getDb();
$exists = false;
if ($this->forceMasterDb) {
$db->useMaster(function ($db) use ($query, $value, &$exists) {
$exists = $this->queryValueExists($query, $value);
});
} else {
$exists = $this->queryValueExists($query, $value);
}
return $exists;
}
/**
* Run query to check if value exists
*
* @param QueryInterface $query
* @param mixed $value the value to be checked
* @return bool
*/
private function queryValueExists($query, $value)
{
if (is_array($value)) {
return $query->count("DISTINCT [[$this->targetAttribute]]") == count($value) ;
}
return $query->exists();
}
/**