Unique Validator Criteria


So I need to check if a user is trying to add a friend who he has already added.

It seems obvious to use the Unique validator, but this causes issues.

My problem is, I need to supply the validator with criteria stating that ‘uid’ must equal the model’s currently set uid attribute.


SELECT id FROM friends WHERE friend = value AND uid = theuid

But when I try set the criteria to use $this->uid, it is null.

Although after validation it is not null and is correctly set.

How does the rules() method get called? Because clearly it isn’t allowing me to access attributes.

How else can I do this without writing my own method.

                        array('friend', 'unique', 'className' => 'Friends', 'attributeName' => 'friend', 'criteria' => array(

                                'condition' => 'uid = :uid',

                                'params' => array(':uid' => $this->uid) // $this->uid is null here


Hello, I had exactly the same problem right now… but I only had the problem, that my criteria was malformed… and with cheating at your code mine works now :)

I’ll post you parts of my solution:

I guess you haven’t set the uid before validation/saving

and $this must work in rules() since it’s just a normal method of your class


     public function rules()


         // NOTE: you should only define rules for those attributes that

         // will receive user inputs.

         return array(

             array('ruleid, from, to', 'required'),


             // from and to must be unique for each rule

             array('from', 'unique',

                 'criteria' => array(

                     'condition' => 'ruleid= :ruleid',

                     'params' => array(':ruleid' => $this->ruleid))),

             array('to', 'unique',

                 'criteria' => array(

                     'condition' => 'ruleid= :ruleid',

                     'params' => array(':ruleid' => $this->ruleid))),


             array('ruleid', 'numerical', 'integerOnly'=>true),

             array('from, to', 'length', 'max'=>255),




        $scenario = 'add';

        $model = new MyModel($scenario);

        if (isset($_POST['MyModel']) && $_POST['MyModel']['scenario'] == $scenario)



            $model->ruleid = $otherModel->id;

            $model->attributes = $_POST['MyModel'];

            if ($model->save())


                // start with a new field if saving was successful

                $scenario = 'add';

                $model = new MyModel($scenario);



        $newField = $model;


        $scenario = 'update';

        if (isset($_POST['MyModel']) && $_POST['MyModel']['scenario'] == $scenario)


            $model = MyModel::model()->findbyPk($_POST['MyModel']['id']);

            $model->attributes = $_POST['MyModel'];



PS: i would vote for simpler unique-syntax something like:

array(‘field1, field2’, ‘unique’)

also setting unique-rules through gii would also be great :)

Well $this should always be accessible within a local non-static method.

I’m setting $model->uid in the controller before calling $model->validate().

Which is why im completely clueless as to why the uid is not set.

Just like yours really, except I set ->attributes before ->uid to prevent users modifying POST data to re-set them.

But yeah, my controller code is the same as yours pretty much except i validate() then save() rather than just save().


So i fixed this with help from rawtaz.

Basically when you set attributes using $model->attributes, it caches values in some way which means uid is null.

So if i set both friend and uid directly, one by one, it works correctly.

Here’s an example for those who have this same issue:

Does not work:

$model->attributes = $_POST['Something'];

$model->user_id = 6;

// Currently user_id is cached as null, but is set as 6


// Validate calls rules() which uses the cached value, null

Works, not secure:

$model->user_id = 6;

$model->attributes = $_POST['Something'];

// Currently user_id is 6, cached as 6 too


// Validate calls rules() which uses the cached value, 6

// Users can edit their POST data to re-set user_id, this is not secure

Works, lengthy:

$model->user_id = $_POST['user_id'];

$model->friend = $_POST['friend'];

// Both are set directly so are cached and set correctly


// Works perfectly

// Problem here is if you have many fields, it becomes a lengthy process of setting each one

wow, i just ran into this – that’s pretty bad :(

The reason for this is described here:


Sorry to bump an old thread, but I had the same issue.

Why not do this?

$_POST['Something']['user_id'] = 6

$model->attributes = $_POST['Something'];


That way it doesn’t matter if the user tampers with the post data because it’s overwritten, and it caches it fine.