1

I'm building a rating system where a user can rate something from 1-5 stars.

I was wondering if there's a way to automatically calculate all of a specific item's ratings (from the ratings table where model='x' and foreign_key='y') on afterSave or something similar.

I can do it in the ratings_controller just fine... just thought it might be more ideal to be done automatically in the model. Can anyone point me in the right direction for this?

I would LOVE to hear that there's some kind of association setting in CakePHP that allows it to do this for you - something like:

//Rating model
var $belongsTo = array(
    'Restaurant' => array(
        'averageValue' => 'rating
    )
);

But - I'm sure that's asking to much :)

Dave
  • 28,833
  • 23
  • 113
  • 183

2 Answers2

2

if you want to save the average into a field in items table then afterSave would probably be the best solution right now.

The only thing cake can automatically do for you is keeping track of how many ratings an item has (counterCache), but not other aggregate functions.

virtualField may be good, but I have never used that for aggregate functions, so I'm not sure. Besides, if your ratings don't change often, it would put unnecessary work on the system.

In Rating model:

function afterSave($created){
   $avgValue = $this->Query('SELECT AVG(rating) as rating FROM ratings WHERE ratings.restaurant_id = '.$this->restaurant_id);
   $this->Restaurant->updateRatingAverage($this->restaurant_id,$avgValue[0][0]['rating']);
}

In Restaurant model

function updateRatingAverage($id,$avg){  
   $this->id = $id;
   $this->field('your_average_field_here',$avg);
}

you might want to log the $avgValue to see how it's structured, but I think I got that right.

Anh Pham
  • 5,431
  • 3
  • 23
  • 27
  • Any chance you could give me a hint on the code I'd use? I'm new to model functions and not sure how I'd update a field of a different model within the Rating model. – Dave Jul 24 '11 at 20:48
  • Cool - this seems like what I'm looking for, thanks. The only down-side is that if I want to allow reviews for other things than restaurants, they each have to have their own function to calc average instead of just doing it in the ratings controller upon adding a new rating. Sounds like I might just stick w/ my original plan. – Dave Jul 24 '11 at 22:03
  • how did you do in the controller? I don't know what else you need, so that's just an example for restaurant. – Anh Pham Jul 24 '11 at 22:29
1

I need to post and answer because I can't yet comment.

So, your (for example) restaurants hasMany ratings? Try virtualField in restaurants model where you calculate the rating every time restaurants are fetched from database. There might be need for GROUP BY like ypercube mentioned if you need to use AVG().

Henri
  • 740
  • 10
  • 22
  • Yes - `restaurant hasMany rating`. If I use a virtualField, it will have to do the AVG calculation (on upwards of tens of thousands of ratings) EVERY time I get data for a restaurant instead of just calculating it whenever a new rating is added and getting the `Restaurant.rating` field. Hardly seems ideal. I can do the calculation just fine in the controller when a rating is saved - just thought there might be some kind of Cake way to do it better / in a more ideal fashion. – Dave Jul 24 '11 at 20:11
  • @Dave: a note (from someone who admittedly does not know Cake too well): *"If you use a virtualField, it will have to do the AVG calculation, EVERY time you get data for a restaurant."* True, but the database may not necessarily need to do any actual calculation because the query may be in the cache. If you ask the rating 10 times in a row, it will only calculate once (when there is no change in the rating data in between and the query has not been removed from teh db cache). – ypercubeᵀᴹ Jul 24 '11 at 22:55
  • ... *"instead of just calculating it whenever a new rating is added and getting the Restaurant.rating field"* True, which also means that you'll do the calculation no matter if you need it or not. In situations when a lot a additions in the rating data happen in a short time, you'll do a new calculation for EVERY addition (afterSave), while you may only need the last (calculation) after all additions are finished. – ypercubeᵀᴹ Jul 24 '11 at 22:58