Composite key exist validator

Hi everyone…

I’ve searched a lot and I’ve never found a specific validator that checks if a composite foreign key exists.

So, I implemented this class and I’d like anyone could test it and give me suggestion how to improve it.

I took as base the Yii’s CExistValidator class.


/**

 * CCompositeExistValidator validates that the composite attribute value exists in a table.

 *

 * This validator is often used to verify that a composite foreign key contains a value

 * that can be found in the foreign table.

 *

 * @author SoulBlighter based on Qiang Xue code

 * @package ext.composite-validators

 */

class CCompositeExistValidator extends CValidator

{

	/**

	 * @var string the ActiveRecord class name that should be used to

	 * look for the attribute value being validated. Defaults to null,

	 * meaning using the ActiveRecord class of the attribute 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.

	 * @since 1.0.8

	 */

	public $criteria=array();

	/**

	 * @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;


	/**

	 * 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 attributes being validated separated by pipe

	 */

	protected function validateAttribute($object,$attribute)

	{

		$attributes = explode('|', $attribute);

		$values = array();

		foreach($attributes as $attr) {

			$values[]=$object->$attr;

		}

		

		$allEmpty = true;

		foreach ($values as $value) {

			if(!($this->allowEmpty && $this->isEmpty($value))) {

				$allEmpty = false;

				break;

			}

		}

		if ($allEmpty) {

			return;

		}


		$className=$this->className===null?get_class($object):Yii::import($this->className);

		

		$attributesName=$this->attributeName===null?$attributes:explode('|', $this->attributeName);

		$finder=CActiveRecord::model($className);

		$table=$finder->getTableSchema();

		

		$columns = array();

		foreach($attributesName as $attributeName) {

			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)));

			}

			$columns[] = $column;

		}


		$conditions = array();

		$params = array();

		foreach($columns as $index=>$column) {

			$conditions[] = $column->name.'=:'.$column->name;

			$params[":{$column->name}"] = $values[$index];

		}

		

		$condition = implode(' AND ',$conditions);

		$criteria=array('condition'=>$condition,'params'=>$params);

		if($this->criteria!==array())

		{

			$criteria=new CDbCriteria($criteria);

			$criteria->mergeWith($this->criteria);

		}


		if(!$finder->exists($criteria))

		{

			$message=$this->message!==null?$this->message:Yii::t('yii','The composite key {attribute} "{value}" is invalid.');

			$this->addError($object,str_replace('|', ',', $attribute),$message,array('{value}'=>implode(',',$values)));

		}

	}

}

I’ve made some tests and worked fine for me.

I tried to define the pattern of separating composite key columns using the pipe symbol.

Below, an example of use:




public function rules() {

	return array(

		array('codigo_empresa_requisitante|codigo_filial_requisitante|matricula_requisitante', 

			'ext.composite-validators.CCompositeExistValidator', 

			'allowEmpty'=>false, 

			'className'=>'Funcionario', 

			'attributeName'=>'codigo_empresa|codigo_filial|matricula'),

	);

}



Someone can tell me how to create an alias to call my validator, because I wanted to use ‘compositeExist’ instead of ‘ext.composite-validators.CCompositeExistValidator’?

Thanks for attention.

Is there someone who saw that has tested? Sorry, but it would be great for me if someone test once.

Seriously? Nobody? :blink:

I would gladly test it, but Spanish, without instructions? Sorry, you’ll have to be more clear on:

1: Do I put it in the extension folder?

2: Can you put this with english instructions?

Specifically, which word does what




public function rules() {

        return array(

                array('codigo_empresa_requisitante|codigo_filial_requisitante|matricula_requisitante', 

                        'ext.composite-validators.CCompositeExistValidator', 

                        'allowEmpty'=>false, 

                        'className'=>'Funcionario', 

                        'attributeName'=>'codigo_empresa|codigo_filial|matricula'),

        );

}



Hi,

I think you can do it by adding this type of rule.

array(‘field1’, ‘unique’, ‘criteria’ => array(‘condition’ => ‘field1=:field1 AND field2=:field2’, ‘params’ => array(’:field1’ => $this->field1, ‘:field2’ => $this->field2))),

Thanks

Aruna

It worked somehow, but didn’t give me errors when trying to insert a duplicate combination.

So, I searched and found the ECompositeUniqueKeyValidatable component-, which turns out to work fine.

That one works with a behaviour. If this is a bad thing , I don’t know.