Yii Framework Forum: composite unique key validation - Yii Framework Forum

Jump to content

  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

composite unique key validation Rate Topic: -----

#1 User is offline   ololo 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 37
  • Joined: 23-January 11
  • Location:Minsk, Belarus

Posted 25 January 2011 - 06:31 AM

Hi,

it seems it's not possible with built-in validators, how I should implement this feature in the model?
0

#2 User is offline   andy_s 

  • Random Member Title
  • Yii
  • Group: Moderators
  • Posts: 1,526
  • Joined: 22-June 09
  • Location:Russia, Kostroma

Posted 25 January 2011 - 07:30 AM

Look at this topic: http://www.yiiframew...site-unique-key
Here qiang says it will be probably supported in Yii 1.1, but I'm not sure if it's already supported. I would do it using a method-based validator.
0

#3 User is offline   ololo 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 37
  • Joined: 23-January 11
  • Location:Minsk, Belarus

Posted 25 January 2011 - 03:29 PM

View Postandy_s, on 25 January 2011 - 07:30 AM, said:

Here qiang says it will be probably supported in Yii 1.1, but I'm not sure if it's already supported.

I didn't find such a feature in 1.1

So, my solution

Validator
<?php

/**
 * CompositeUniqueKeyValidator class file.
 */
class CompositeUniqueKeyValidator extends CValidator {
    /**
     * @var string comma separated columns that are unique key
     */
    public $keyColumns;

    public $errorMessage = '"{columns_labels}" are not unique';

    /**
     * @var boolean whether the error message should be added to all of the columns
     */
    public $addErrorToAllColumns = false;

    /**
     * @param CModel $object the object being validated
     * @param string $attribute if there is an validation error then error message
     * will be added to this property
     */
    protected function validateAttribute($object, $attribute) {
        $class = get_class($object);
        Yii::import($class);

        $keyColumns = explode(',', $this->keyColumns);
        if (count($keyColumns) == 1) {
            throw new CException('CUniqueValidator should be used instead');
        }
        $columnsLabels = $object->attributeLabels();

        $criteria = new CDbCriteria();
        $keyColumnsLabels = array();
        foreach ($keyColumns as &$column) {
            $column = trim($column);
            $criteria->compare($column, $object->$column);
            $keyColumnsLabels[] = $columnsLabels[$column];
        }
        unset($column);
        $criteria->limit = 1;

        if ($class::model()->count($criteria)) {
            $message = Yii::t('yii', $this->errorMessage, array(
                '{columns_labels}' => join(', ', $keyColumnsLabels)
            ));
            if ($this->addErrorToAllColumns) {
                foreach ($keyColumns as $column) {
                    $this->addError($object, $column, $message);
                }
            }
            else {
                $this->addError($object, $attribute, $message);
            }
        }
    }

}

?>


Model
    public function rules() {
        return array(
            array('applicationId', 'CompositeUniqueKeyValidator', 'keyColumns' => 'user, applicationId'),
        );
    }


It looks good when we can attach error message to the certain property (when 'addErrorToAllColumns' is false) e.g. user must be unique within the application (composite unique key - user+applicationId)
4

#4 User is offline   fouss 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 385
  • Joined: 05-October 10
  • Location:Bamako Mali

Posted 27 January 2011 - 05:29 AM

View Postololo, on 25 January 2011 - 03:29 PM, said:

I didn't find such a feature in 1.1

So, my solution

Validator
<?php

/**
 * CompositeUniqueKeyValidator class file.
 */
class CompositeUniqueKeyValidator extends CValidator {
    /**
     * @var string comma separated columns that are unique key
     */
    public $keyColumns;

    public $errorMessage = '"{columns_labels}" are not unique';

    /**
     * @var boolean whether the error message should be added to all of the columns
     */
    public $addErrorToAllColumns = false;

    /**
     * @param CModel $object the object being validated
     * @param string $attribute if there is an validation error then error message
     * will be added to this property
     */
    protected function validateAttribute($object, $attribute) {
        $class = get_class($object);
        Yii::import($class);

        $keyColumns = explode(',', $this->keyColumns);
        if (count($keyColumns) == 1) {
            throw new CException('CUniqueValidator should be used instead');
        }
        $columnsLabels = $object->attributeLabels();

        $criteria = new CDbCriteria();
        $keyColumnsLabels = array();
        foreach ($keyColumns as &$column) {
            $column = trim($column);
            $criteria->compare($column, $object->$column);
            $keyColumnsLabels[] = $columnsLabels[$column];
        }
        unset($column);
        $criteria->limit = 1;

        if ($class::model()->count($criteria)) {
            $message = Yii::t('yii', $this->errorMessage, array(
                '{columns_labels}' => join(', ', $keyColumnsLabels)
            ));
            if ($this->addErrorToAllColumns) {
                foreach ($keyColumns as $column) {
                    $this->addError($object, $column, $message);
                }
            }
            else {
                $this->addError($object, $attribute, $message);
            }
        }
    }

}

?>


Model
    public function rules() {
        return array(
            array('applicationId', 'CompositeUniqueKeyValidator', 'keyColumns' => 'user, applicationId'),
        );
    }


It looks good when we can attach error message to the certain property (when 'addErrorToAllColumns' is false) e.g. user must be unique within the application (composite unique key - user+applicationId)


I have this error

Parse error: syntax error, unexpected T_PAAMAYIM_NEKUDOTAYIM in /home/www/ed3a58441fae69684a5a8204d2f03575/web/centre10/protected/models/CompositeUniqueKeyValidator.php on line 44

Posted Image
1

#5 User is offline   zaccaria 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 2,232
  • Joined: 04-October 09
  • Location:Moscow

Posted 27 January 2011 - 05:35 AM

This jewish error follows a typo, maybe you have forgot some brackets.

I usually have when I type:

MyModel::model->findAll()

Instead of

MyModel::model()->findAll()
0

#6 User is offline   fouss 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 385
  • Joined: 05-October 10
  • Location:Bamako Mali

Posted 27 January 2011 - 05:47 AM

The error line is

 if ($class::model()->count($criteria)) {

Posted Image
0

#7 User is offline   fouss 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 385
  • Joined: 05-October 10
  • Location:Bamako Mali

Posted 27 January 2011 - 05:58 AM

View Postfouss, on 27 January 2011 - 05:47 AM, said:

The error line is

 if ($class::model()->count($criteria)) {



I have this error on remote computer.
My local machine run the script without error
Posted Image
0

#8 User is offline   andy_s 

  • Random Member Title
  • Yii
  • Group: Moderators
  • Posts: 1,526
  • Joined: 22-June 09
  • Location:Russia, Kostroma

Posted 27 January 2011 - 08:30 AM

View Postfouss, on 27 January 2011 - 05:58 AM, said:

I have this error on remote computer.
My local machine run the script without error

I think the reason is PHP version. On your local machine you have 5.3 installed and some older version on remote computer.
Try this syntax:

CActiveRecord::model($class)->count($criteria)

2

#9 User is offline   fouss 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 385
  • Joined: 05-October 10
  • Location:Bamako Mali

Posted 27 January 2011 - 09:52 AM

View Postandy_s, on 27 January 2011 - 08:30 AM, said:

I think the reason is PHP version. On your local machine you have 5.3 installed and some older version on remote computer.
Try this syntax:

CActiveRecord::model($class)->count($criteria)



Thank you very much............. It's working now!
Posted Image
0

#10 User is online   tri 

  • Elite Member
  • Yii
  • Group: Moderators
  • Posts: 1,651
  • Joined: 20-November 08
  • Location:Stockholm, Sweden

Posted 27 January 2011 - 10:13 AM

View Postzaccaria, on 27 January 2011 - 05:35 AM, said:

This jewish error follows a typo, maybe you have forgot some brackets.

I usually have when I type:

MyModel::model->findAll()

Instead of

MyModel::model()->findAll()


This is the explanation from the PHP manual

Quote

Paamayim Nekudotayim would, at first, seem like a strange choice for naming a double-colon. However, while writing the Zend Engine 0.5 (which powers PHP 3), that's what the Zend team decided to call it. It actually does mean double-colon - in Hebrew!

So the error message should be read "unexpected double-colon ..."

/Tommy
Don't forget to read The Definitive Guide to Yii (en) (sv) | The class reference has the details
1

#11 User is offline   fouss 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 385
  • Joined: 05-October 10
  • Location:Bamako Mali

Posted 27 January 2011 - 10:34 AM

View Posttri, on 27 January 2011 - 10:13 AM, said:

This is the explanation from the PHP manual

So the error message should be read "unexpected double-colon ..."

/Tommy


thanks gay!
Posted Image
0

#12 User is offline   rahul.vit09 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 48
  • Joined: 07-February 11

Posted 12 February 2011 - 01:44 AM

View Postololo, on 25 January 2011 - 03:29 PM, said:

I didn't find such a feature in 1.1

So, my solution

Validator
<?php

/**
 * CompositeUniqueKeyValidator class file.
 */
class CompositeUniqueKeyValidator extends CValidator {
    /**
     * @var string comma separated columns that are unique key
     */
    public $keyColumns;

    public $errorMessage = '"{columns_labels}" are not unique';

    /**
     * @var boolean whether the error message should be added to all of the columns
     */
    public $addErrorToAllColumns = false;

    /**
     * @param CModel $object the object being validated
     * @param string $attribute if there is an validation error then error message
     * will be added to this property
     */
    protected function validateAttribute($object, $attribute) {
        $class = get_class($object);
        Yii::import($class);

        $keyColumns = explode(',', $this->keyColumns);
        if (count($keyColumns) == 1) {
            throw new CException('CUniqueValidator should be used instead');
        }
        $columnsLabels = $object->attributeLabels();

        $criteria = new CDbCriteria();
        $keyColumnsLabels = array();
        foreach ($keyColumns as &$column) {
            $column = trim($column);
            $criteria->compare($column, $object->$column);
            $keyColumnsLabels[] = $columnsLabels[$column];
        }
        unset($column);
        $criteria->limit = 1;

        if ($class::model()->count($criteria)) {
            $message = Yii::t('yii', $this->errorMessage, array(
                '{columns_labels}' => join(', ', $keyColumnsLabels)
            ));
            if ($this->addErrorToAllColumns) {
                foreach ($keyColumns as $column) {
                    $this->addError($object, $column, $message);
                }
            }
            else {
                $this->addError($object, $attribute, $message);
            }
        }
    }

}

?>


Model
    public function rules() {
        return array(
            array('applicationId', 'CompositeUniqueKeyValidator', 'keyColumns' => 'user, applicationId'),
        );
    }


It looks good when we can attach error message to the certain property (when 'addErrorToAllColumns' is false) e.g. user must be unique within the application (composite unique key - user+applicationId)

thank you very much for this compositeUniqueKeyValidator class. I am highly appreciating your work. Superb Job It solved my problem. thank you again.

And I think It should be added to Yii next version.
0

#13 User is offline   phazei 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 62
  • Joined: 20-July 09

Posted 07 March 2011 - 05:40 PM

I looked and it seems no one every submitted a ticket for it, so it fell through the cracks.

Done:
http://code.google.c.../detail?id=2184
0

#14 User is offline   Wiseon3 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 13
  • Joined: 14-February 11

Posted 16 March 2011 - 08:23 AM

It works perfectly!
Thanks for the validator class ololo :)
0

#15 User is offline   rAWTAZ 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 102
  • Joined: 08-January 10

Posted 19 March 2011 - 09:47 AM

Hey there,

This looks interesting, I was just searching to see if there was something ready-made.

ololo; Have you considered releasing this as a formal Yii extension? I looked in the repository and can only find http://www.yiiframew...olumns-validate for the keyword "unique", and it seems that one is not this one :) Yours seem a bit more updated.

Thanks,
0

#16 User is offline   iota 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 17
  • Joined: 07-January 11
  • Location:Auckland, New Zealand

Posted 03 May 2011 - 04:26 AM

Great bit of code, thanks for this!

I have one problem though - how do I prevent this validation running when updating an existing record and none of the composite keys have changed. It seems to be validating against itself, whereas I only want it to apply the composite validation if either or both of the composite key values have changed.

Hope this makes sense :)
0

#17 User is offline   Mas Gagah 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 6
  • Joined: 21-December 09

Posted 16 May 2011 - 11:08 AM

its work well on insert, but also validate when update.
how do i ignore this validation while execute update?
0

#18 User is offline   M Hussain 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 3
  • Joined: 05-January 11

Posted 06 June 2011 - 05:37 AM

View PostMas Gagah, on 16 May 2011 - 11:08 AM, said:

its work well on insert, but also validate when update.
how do i ignore this validation while execute update?

Same for me tooo. How to skip validation on update.
0

#19 User is offline   jeremy 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 57
  • Joined: 17-June 09
  • Location:Massachusetts, USA

Posted 07 June 2011 - 01:12 PM

In terms of handling existing records, there are 3 situations to consider:
1) a new record
2) an existing record, where none of the UK fields have changed
3) an existting record, where one or more of the UK fields have changed

In the case of scenario 1) and 3), then we should verify there are zero rows in the database with the same UK fields
In the case of scenario 2), we can allow a single record to exist in the database, namely the previous version of this record.

The problem is that in order to distinguish scenario 2) from 3), you need to track the oldAttributes. I already have this in place in my application, for automatic changeLog creation:

class MyActiveRecord extends CActiveRecord {
	public $_oldAttributes = array();

	public function afterFind() {
    	$this->setOldAttributes($this->getAttributes());
	}

	public function getOldAttributes() {
    	return $this->_oldattributes;
	}

	public function setOldAttributes($value) {
    	$this->_oldattributes = $value;
	}
 }


All my models extend the above class.

Now, the modified code to make use of oldAttributes. Note that I renamed the class slightly and referenced this forum topic. changes from the above version are flagged with my initials 'JJD':

<?php

/**
 * CompositeUniqueKeyValidator class file.
 * from http://www.yiiframew...key-validation/
 */
class ECompositeUniqueKeyValidator extends CValidator {

	/**
 	* @var string comma separated columns that are unique key
 	*/
	public $keyColumns;
	public $errorMessage = '"{columns_labels}" are not unique';
	/**
 	* @var boolean whether the error message should be added to all of the columns
 	*/
	public $addErrorToAllColumns = false;

	/**
 	* @param CModel $object the object being validated
 	* @param string $attribute if there is an validation error then error message
 	* will be added to this property
 	*/
	protected function validateAttribute($object, $attribute) {

    	$class = get_class($object);
    	Yii::import($class);

    	$keyColumns = explode(',', $this->keyColumns);
    	if (count($keyColumns) == 1) {
        	throw new CException('CUniqueValidator should be used instead');
    	}
    	$columnsLabels = $object->attributeLabels();
    	$oldAttributes = $object->getOldAttributes();   // +JJD

    	$criteria = new CDbCriteria();
    	$keyColumnsLabels = array();
    	$changedKeyColumns = false;  // +JJD
    	foreach ($keyColumns as &$column) {
        	$column = trim($column);
        	$criteria->compare($column, $object->$column);
        	$keyColumnsLabels[] = $columnsLabels[$column];
        	if (!$changedKeyColumns && !$object->isNewRecord && ($oldAttributes[$column] != $object->$column)) {
            	$changedKeyColumns = true;  // +JJD
        	}
    	}
    	unset($column);

    	// handle existing records
    	//$criteria->limit = 1;
    	$allowed = ($object->isNewRecord || $changedKeyColumns ? 0 : 1);  // +JJD
    	//if ($class::model()->count($criteria)) {
    	$count = CActiveRecord::model($class)->count($criteria);  // +JJD
    	// +JJD  check whether we have too many rows in the database
    	if ($count > $allowed) {
        	$message = Yii::t('yii', $this->errorMessage, array('{columns_labels}' => join(', ', $keyColumnsLabels)));
        	if ($this->addErrorToAllColumns) {
            	foreach ($keyColumns as $column) {
                	$this->addError($object, $column, $message);
            	}
        	} else {
            	$this->addError($object, $attribute, $message);
        	}
    	}
	}

}

?>

2

#20 User is offline   ololo 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 37
  • Joined: 23-January 11
  • Location:Minsk, Belarus

Posted 12 June 2011 - 02:31 PM

I removed my last post with lots of code, and as I promised, I created the extension - http://www.yiiframew...ey-validatable/

Thanks
0

Share this topic:


  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users