Custom Validation - When to Call?

Hello Yii-ers,

I have a model for reviews. There should be only one review per object per user. I have a joiner table with:

review_id, user_id, parent_id

review_id is the id of the users review

user_id is the users id

parent_id is the article id the user reviewed

I then set the following validation rule in the review model:

array(‘id’,‘DuplicateReview’),

Then created a custom validator:




	/**

	 * Duplicate Review Validator

 	* @param string $attribute the name of the attribute to be validated

 	* @param array $params options specified in the validation rule

 	*/

	public function DuplicateReview($attribute,$params)

	{

		$count = Blog::model()->checkReviewAbility($this->parent_id);

		if($count->ability > 0)

		{

			$message = Yii::t('Review.Model', 'Thank you for your review! But, you have already added a review for ' . $this->title . '.');

			$this->addError($attribute, $message);

		}

	}



The checkReviewAbility function is in the blog model and has the following code:




	/**

	 * Check to see if user has already added a review for this object

	 */

	public function checkReviewAbility($parentId = 0)

	{

		$command = Yii::app()->db->createCommand()

				->select('count(*)as ability')

				->from('{{review_user}}')

				->where('parent_id=:parentid and user_id=:userid', array(':parentid'=>$parentId, ':userid'=>Yii::app()->user->id))

				->queryRow();

		

		return $command;

	}



What I am trying to accomplish is when a user goes to add a new review checkReviewAbility should be called. If it returns 0 then this user hasn’t reviewed this article and it should save. If checkReviewAbility > 0 then the user has already reviewed this article and they should be presented with an error message.

I’m trying to go about this using a test method to try and create duplicate reviews. Having the first one succeed and the second one fail.

I created the following test:




<?php


class ReviewTest extends CDbTestCase

{

	//fixture connection

	public $fixtures=array(

				'blogs'=>'Blog',

				'comments'=>'ReviewComment',

				'reviews'=>'Review',

				'users'=>'User',

				'authAssign'=>':AuthAssignment',

		);

		

	public function testCreateArticleReview()

	{

		//insert a new review on an article

		$review = new Review();

		

		$review->setAttributes(array(

			'parent_id'=>1,

			'title'=>'Article Review',

			'content'=>'I liked this a lot',

			'status'=>Review::STATUS_PENDING,

			'rating'=>5,

			//'create_user_id'=>1

			//'create_time'=>time(),

			//'update_user_id'=>'',

			//'update_time'=>'',

			

		));

	

		Yii::app()->user->setId($this->users('user1')->id);

		$this->assertTrue($review->save());

		

	}

	

	public function testDuplicateReview()

	{

		//tries to enter a review for user and parent id that already exists.

		//		The save should be false based on duplicateReview validator

		$review = new Review();

		

		$review->setAttributes(array(

			'parent_id'=>1,

			'title'=>'Article Review2',

			'content'=>'I liked this a lot2',

			'status'=>Review::STATUS_PENDING,

			'rating'=>5,

			//'create_user_id'=>1

			//'create_time'=>time(),

			//'update_user_id'=>'',

			//'update_time'=>'',

			

		));

		

		Yii::app()->user->setId($this->users('user1')->id);

		var_dump(Blog::model()->checkReviewAbility(1));

		$this->assertFalse($review->save());

		

	}

	

}



When I var_dump the array shown has

array(1) {

["ability"]=>

string(1) "1"

}

so, this is greater then 1. So, the save should fail on validation on the second save (testDuplicateReview). But it asserts true and further more it seems to update/overwrite the original review.

I honestly have no idea what I am doing wrong. If my checkReviewAbility is written correctly or if it is the validator that is wrong.

Any help would be much appreciated!

Thank you!

Well, I feel like an idiot! I figured it out…

The query was returning an array not an object. I was trying to call it like an object.

I changed the line:




if($count->ability > 0)

                {

                        $message = Yii::t('Review.Model', 'Thank you for your review! But, you have already added a review for ' . $this->title . '.');

                        $this->addError($attribute, $message);

                }



TO




if($count[ability] > 0)

                {

                        $message = Yii::t('Review.Model', 'Thank you for your review! But, you have already added a review for ' . $this->title . '.');

                        $this->addError($attribute, $message);

                }




And in preliminary tests, its working fine. Now to start working with it in a view!