diff options
Diffstat (limited to 'framework/validators/CUniqueValidator.php')
| -rw-r--r-- | framework/validators/CUniqueValidator.php | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/framework/validators/CUniqueValidator.php b/framework/validators/CUniqueValidator.php new file mode 100644 index 0000000..d48b35f --- /dev/null +++ b/framework/validators/CUniqueValidator.php @@ -0,0 +1,129 @@ +<?php +/** + * CUniqueValidator class file. + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @link http://www.yiiframework.com/ + * @copyright Copyright © 2008-2011 Yii Software LLC + * @license http://www.yiiframework.com/license/ + */ + +/** + * CUniqueValidator validates that the attribute value is unique in the corresponding database table. + * + * When using the {@link message} property to define a custom error message, the message + * may contain additional placeholders that will be replaced with the actual content. In addition + * to the "{attribute}" placeholder, recognized by all validators (see {@link CValidator}), + * CUniqueValidator allows for the following placeholders to be specified: + * <ul> + * <li>{value}: replaced with current value of the attribute.</li> + * </ul> + * + * @author Qiang Xue <qiang.xue@gmail.com> + * @version $Id: CUniqueValidator.php 3549 2012-01-27 15:36:43Z qiang.xue $ + * @package system.validators + * @since 1.0 + */ +class CUniqueValidator extends CValidator +{ + /** + * @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 + */ + 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 + */ + 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. + */ + 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. + * @param CModel $object the object being validated + * @param string $attribute the attribute being validated + */ + protected function validateAttribute($object,$attribute) + { + $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}'=>CHtml::encode($value))); + } + } +} + |
