To validate composite unique keys attach the ECompositeUniqueKeyValidateable behavior, declare unique keys and declare short validation method in a model class.
There are few reasons to use behavior and validation method instead of writing validation class
we can't attach a handler for CActiveRecord::onAfterFind() with only validator (this is needed for storing of old attributes of the model for proper validation when updating an existing record)
CValidator doesn't imply validation of several attributes
Tested on Yii 1.1 and php 5.3
Attach the ECompositeUniqueKeyValidatable behavior and declare unique keys
public function behaviors() { return array( 'ECompositeUniqueKeyValidatable' => array( 'class' => 'ECompositeUniqueKeyValidatable', 'uniqueKeys' => array( 'attributes' => 'login, applicationId', 'errorMessage' => 'Your login is already taken' ) ), ); }
declare simple validation method in the model class
/** * Validates composite unique keys * * Validates composite unique keys declared in the * ECompositeUniqueKeyValidatable bahavior */ public function compositeUniqueKeysValidator() { $this->validateCompositeUniqueKeys(); }
declare the validation rule
public function rules() { return array( // the first parameter doesn't matter, I use '*' (pretty ugly // definition, but I don't know a better way) array('*', 'compositeUniqueKeysValidator'), ); }
attributes - unique key
errorMessage - error message
errorAttributes (optional) - attributes of the model which will contain the error message
skipOnErrorIn (optional) - if one of this attributes contains errors then validation will be skipped
declaring of two composite unique keys
public function behaviors() { return array( 'ECompositeUniqueKeyValidatable' => array( 'class' => 'ECompositeUniqueKeyValidatable', 'uniqueKeys' => array( array( 'attributes' => 'email, applicationId', 'errorAttributes' => 'email, email_confirmation', 'errorMessage' => 'This email is already registered', 'skipOnErrorIn' => 'email, applicationId' ), array( 'attributes' => 'login, applicationId', 'errorAttributes' => 'login', 'errorMessage' => 'Your login is already taken', 'skipOnErrorIn' => 'login, applicationId' ), ) ), // ...
Total 20 comments
the deprecated "Call-time pass-by-reference" will fail completely in php 5.4
Building on Sebastian K.'s work, I altered:
sieppl, I have just figured out why I don't see "call-time pass-by-reference" warnings: allow_call_time_pass_reference php.ini option should be disabled
now with this option disabled I can see that everything is allright with the extension and I don't use "call-time pass-by-reference" so I reverted back the last commit and updated the extension again
let me clarify
"call-time pass-by-reference" is when we do this
and this is really dangerous to pass something by reference in a function whose argument is not supposed to be passed by reference
see http://php.net/manual/en/ini.core.php
but when we do something like this then it's OK and there is no warning:
Sorry, I was not precise enough. You are doing a "Call-time pass-by-reference" (inside the for loops) which is deprecated since 5.3: http://php.net/manual/en/migration53.deprecated.php
So actually this is not a "bug", but a problem when your parser raises E_DEPRECATED.
could you please describe this problem or provide a php.net link to the bug report or make some example reproducing this problem?
just curious, sometimes people are confused with by-reference loops and call expected behavior "buggy" e.g. https://bugs.php.net/bug.php?id=39307
There are problems with PHP 5.3, when you use the call by reference in combination with for-loops. Following fixes make it work:
and
Please update this extension. Beside this minor issue it works very nice in 5.3, thank you!!!
Hi.
Thank you for your support. I'm using (http://www.yiiframework.com/extension/multimodelform "multimodel-form") and I'm trying to use your extension with the detail model. I think the problem is there.
I'll investigate and let you know my findings.
Regards.
> Question (since I'm a newbie): Shouldn't everybody implementing
> an event call parent before they proceed?
hm, I thought no, but now I'm in doubt about it
I think you can only break something by overriding afterFind() in your model because only CActiveRecord::afterFind() raises an event (so it have to be called through the parent:: keyword if you want to raise an event), and when it's raised all subscribers (including behaviors) are getting notified and perform their logic independently. So I think it's not about another behavior, but I'm not sure.
Try to define ECompositeUniqueKeyValidateable prior to your another behavior and check if afterFind() handler of another behavior is called.
Thank you ololo.
Yes, I do use another behavior which implements afterFind.
Should I put parent::afterFind in that behaviour implementation and in yours too?
Question (since I'm a newbie): Shouldn't everybody implementing an event call parent before they proceed?
Regards.
Hi, oldValue should be initialized for each of the unique keys in afterFind() event handler defined in the behavior
So I can assume that ECompositeUniqueKeyValidatable::afterFind() wasn't called, e.g. it can happen because you have overriden afterFind() event handler without calling parent::afterFind().
Anyway, first make sure ECompositeUniqueKeyValidatable::afterFind() is called (for example by putting die('smth') in the afterFind() method of the behavior)
Hi.
I', having this error while testing your extension.
Any idea?
Regards.
I got one thing
The problem occur only when I use dependent dropdown boxes. (Working fine in normal cause).
In this cause, your extension works as expected. Its excellent
But
In this cause, we are facing problem when update without any change
I will send you the code by mail If you want
Thank you very much
Hm, sorry, but I have no idea what can cause this problem. If you provide me some more information so that I can reproduce the problem, then I'll try to help.
No records as you told. I checked. Got the same error even when I try to update (without any changes) which I create just now
Hi, make sure that there is no other records with the same unique key as the record you are updating (that can happen if you had created several records before you implemented validation).
I got unique error when I try to update a record without changing values of the record. I think its treat as a new record.
anywhere you want, you can use path alias in behavior definition or rely on autoload
Also, where does the behaviors method go?
then you could create switch "useValidationEvent" which will allow to choose between hidden usage of onBeforeValidation/onAfterValidation (default) and disable this feature to use explicit validation.
...as you can see I prefer solution "less code and modyfications is better in most cases, but let there be a switch to fully manual mode for advanced scenarios" ;-)
yes, but in this case users will not be able to set order in which validators should be performed, and undeclared validation in onAfterValidation() method looks messy to me, I prefer more explicit ways
maybe you could use onBeforeValidate or onAfterValidate events instead of making users to provide their own wrapper of your mechanizm (compositeUniqueKeysValidator)?
Leave a comment
Please login to leave your comment.