UniqueValidator.php 4.24 KB
Newer Older
w  
Qiang Xue committed
1 2 3 4 5
<?php
/**
 * CUniqueValidator class file.
 *
 * @link http://www.yiiframework.com/
w  
Qiang Xue committed
6
 * @copyright Copyright &copy; 2008-2012 Yii Software LLC
w  
Qiang Xue committed
7 8 9
 * @license http://www.yiiframework.com/license/
 */

w  
Qiang Xue committed
10 11
namespace yii\validators;

w  
Qiang Xue committed
12 13 14 15 16 17 18 19
/**
 * CUniqueValidator validates that the attribute value is unique in the corresponding database table.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: CUniqueValidator.php 3260 2011-06-13 20:56:54Z alexander.makarow $
 * @package system.validators
 * @since 1.0
 */
w  
Qiang Xue committed
20
class CUniqueValidator extends Validator
w  
Qiang Xue committed
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
{
	/**
	 * @var boolean whether the comparison is case sensitive. Defaults to true.
	 * Note, by setting it to false, you are assuming the attribute type is string.
	 */
	public $caseSensitive = true;
	/**
	 * @var boolean whether the attribute value can be null or empty. Defaults to true,
	 * meaning that if the attribute is empty, it is considered valid.
	 */
	public $allowEmpty = true;
	/**
	 * @var string the ActiveRecord class name that should be used to
	 * look for the attribute value being validated. Defaults to null, meaning using
	 * the class of the object currently being validated.
	 * You may use path alias to reference a class name here.
	 * @see attributeName
	 * @since 1.0.8
	 */
	public $className;
	/**
	 * @var string 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
	 * @since 1.0.8
	 */
	public $attributeName;
	/**
	 * @var array additional query criteria. This will be combined with the condition
	 * that checks if the attribute value exists in the corresponding table column.
	 * This array will be used to instantiate a {@link CDbCriteria} object.
	 * @since 1.0.8
	 */
	public $criteria = array();
	/**
	 * @var string the user-defined error message. The placeholders "{attribute}" and "{value}"
	 * are recognized, which will be replaced with the actual attribute name and value, respectively.
	 */
	public $message;
	/**
	 * @var boolean whether this validation rule should be skipped if when there is already a validation
	 * error for the current attribute. Defaults to true.
	 * @since 1.1.1
	 */
	public $skipOnError = true;


	/**
	 * Validates the attribute of the object.
	 * If there is any error, the error message is added to the object.
w  
Qiang Xue committed
72
	 * @param \yii\base\Model $object the object being validated
w  
Qiang Xue committed
73 74
	 * @param string $attribute the attribute being validated
	 */
w  
Qiang Xue committed
75
	public function validateAttribute($object, $attribute)
w  
Qiang Xue committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
	{
		$value = $object->$attribute;
		if ($this->allowEmpty && $this->isEmpty($value))
			return;

		$className = $this->className === null ? get_class($object) : Yii::import($this->className);
		$attributeName = $this->attributeName === null ? $attribute : $this->attributeName;
		$finder = CActiveRecord::model($className);
		$table = $finder->getTableSchema();
		if (($column = $table->getColumn($attributeName)) === null)
			throw new CException(Yii::t('yii', 'Table "{table}" does not have a column named "{column}".',
				array('{column}' => $attributeName, '{table}' => $table->name)));

		$columnName = $column->rawName;
		$criteria = new CDbCriteria(array(
			'condition' => $this->caseSensitive ? "$columnName=:value" : "LOWER($columnName)=LOWER(:value)",
			'params' => array(':value' => $value),
		));
		if ($this->criteria !== array())
			$criteria->mergeWith($this->criteria);

		if (!$object instanceof CActiveRecord || $object->isNewRecord || $object->tableName() !== $finder->tableName())
			$exists = $finder->exists($criteria);
		else
		{
			$criteria->limit = 2;
			$objects = $finder->findAll($criteria);
			$n = count($objects);
			if ($n === 1)
			{
				if ($column->isPrimaryKey)  // primary key is modified and not unique
					$exists = $object->getOldPrimaryKey() != $object->getPrimaryKey();
				else
				{
					// non-primary key, need to exclude the current record based on PK
					$exists = array_shift($objects)->getPrimaryKey() != $object->getOldPrimaryKey();
				}
			}
			else
				$exists = $n > 1;
		}

		if ($exists)
		{
			$message = $this->message !== null ? $this->message : Yii::t('yii', '{attribute} "{value}" has already been taken.');
			$this->addError($object, $attribute, $message, array('{value}' => $value));
		}
	}
}