Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
The multi-axis patch in http://drupal.org/node/185074 does not provide for calculating the average of the axes. This is important for other users of the votingAPI, such as Views, which would otherwise be unable to sort by average rating. The VotingAPI documentation says averaging over multiple axes is the API-user's responsibility, and recommends the use of the 'vote' default tag for the average. I added the following rather rough code to the end of _fivestar_cast_vote() (after the new vote is stored)..
// Calculate and set the user's new average vote (for multiaxis).. this could be done earlier to avoid a second call to set_votes
if($tag!='vote'){
$all_user_votes = votingapi_select_votes(array('uid' => $user->uid, 'tag' => NULL) + $criteria);
$vote_sum = 0;
$vote_count = 0;
foreach($all_user_votes as $id => $user_vote) {
if($user_vote['tag']!='vote') {
$vote_sum += $user_vote['value'];
$vote_count++;
} else {
$average_vote=array($user_vote);
}
}
$criteria = array('value' => $vote_sum/$vote_count, 'tag' => 'vote') + $criteria;
votingapi_set_votes($criteria, $average_vote);
}
Comments
Comment #1
quicksketchWe'll need to give this a bit more thought. I don't think it's going to be a safe assumption that all users want to automatically average their votes and I'd like to make a more efficient method to casting these votes. Of course if the votes are handled by the Direct Voting Widget (it's not yet possible, but it should be soon), then each vote is going to have to be handled individually as the user clicks.
It's worth mentioning that you can do this manually and without modifying Fivestar by implementing hook_votingapi_insert(), then you can use the code you've posted above almost exactly within a custom module.
Comment #2
thatistosay CreditAttribution: thatistosay commentedI'm sure more thought will be needed :) I just wanted to raise the issue, since some averaging mechanism will be needed. I haven't looked very thoroughly at the votingapi_hook_insert possibility.. I was aiming for just getting things running quickly when I hacked this together!
The issue of handling each vote as the user clicks is, I think, covered here. I based my node widget-display code on the example code in the multi-axis Issue, and the votes are properly handled via 'ajax' as a result of the existing multi-axis patch. Seems to work fine, the average being calculated anew each time the user clicks a star.
Comment #3
najibx CreditAttribution: najibx commentedeach criteria may have different percentage, rather than simple average i.e $vote_sum/$vote_count
But I agree, there should be a mechanism for averaging somehow.
Comment #4
crea CreditAttribution: crea commenteddupe
Comment #5
crea CreditAttribution: crea commentedThis probably should go to some sort of hook. So each module or user could implement own mode of averaging
Comment #6
crea CreditAttribution: crea commentedComment #7
crea CreditAttribution: crea commentedSo for inserting and deleting average votes on 'vote' axis both hook_votingapi_insert() and hook_votingapi_delete() must be implemented.
Do we need to check for content_id in these hooks, i.e. can $votes array contain votes for different content when hook is running ?
I mean, if $votes array consists of only single vote, everything is simple, but what about complex scenarios where $votes array consists of multiple completely different votes ? Are these scenarios possible with VotingAPI and do we need to support them ?
Comment #8
crea CreditAttribution: crea commentedI adapted code posted by thatistosay for hook_votingapi_insert. The code works for me. I'll look into it more, meanwhile I would be happy if someone reviewed this.
Comment #9
crea CreditAttribution: crea commentedAnother issue is deleting average votes. Fivestar itself is bad at this: it only deletes votes for the single specified axis. Hook_votingapi_delete also seems to be bad option for this: we cannot know what is happening because it's called before actual deletes ( see votingapi_delete_votes() ). So it's getting complicated.
Would it be better idea to make "dummy" CCK-Fivestar field that would serve only as tool to trigger "average" updates ? It wouldn't allow to enter any values, just simple settings.
How about dummy field, that allows user to select one of the already attached Computed Fields as source of average value ? So it would be flexible enough and user would be able to "calculate average" any way he wants!
Unfortunatly I have very little experience of CCK, so I have questions about it. Is it possible to create such field module so it could be run after all fivestar cck fields ? Will setting "dummy field" module weight higher that Fivestar work for this ?
Comment #10
crea CreditAttribution: crea commentedAfter some thought, I think best approach would be to forget about hook_votingapi_insert and hook_votingapi_delete.
They are too general for this rather simple voting scenario. VotingAPI can have many applications and these hooks are called on every events. Because of that using them does not help, and only adds additional layer of complexity - we need to check for context inside hooks. So in simple multi-axis vote scenario, where we have "review" node and "target node" rating is only altered by review nodes - why not just use general nodeapi hooks for review nodes ? If "review" node is updated recalculate "average", when node is deleted - remove "average" vote. It already works so with fivestar cck fields, so same approach would work best for additional "average" vote.
Until we have tight and unified multi-axis configuration system in Fivestar, multi-axis voting remains "build your own system"-type feature. So casual drupalers can just use Rules module (Workflow-NG for 5.x) and don't mess with any custom modules.
Comment #11
quicksketchMoving this to active, since there aren't any real patches to review in this issue. I've branched the 2.x version (now in HEAD), so if we're wanting this feature, it's the perfect time to figure it out since we can break old APIs. I'm still not sure if this would be included at all, and I'm at least not planning on implementing it.
Comment #12
crea CreditAttribution: crea commentedquicksketch, if you want this to be feature of fivestar, then best would be to provide additional "dummy" CCK fivestar field that works like computed field, only for fivestar. Field setting would include single PHP evaluate field where user must insert code for calculating average field. Also "Average" name is not needed anymore, since that field will allow to calculate additional "dummy" value using any formula. Call it "overall rating" or something like that. I think this can be done very quickly using fivestar.module as source. Just need to make sure code for that field runs after all other fivestar CCK fields.
I would code it myself, but after some research I have found that most simple way is to use Rules module :)
Comment #13
davedg629 CreditAttribution: davedg629 commentedI am very interested in this feature request. I have a multi-axis rating system set up and I use code in my node template file to display the averages of each rating axis (call this the "Overall Rating"). The problem is that I cannot sort nodes by the "Overall Rating" in a View because it is not a field of the node. I have tried many avenues to put this code in a cck field but nothing has worked. This has left me with a rating system that calculates an "Overall Rating", but cannot sort by "Overall Rating" in a table View.
Can you explain how you used the Rules module to calculate an "Overall Rating"?
Comment #14
crea CreditAttribution: crea commentedUse computed field, also you need to save it's values in database, that way it should be available in Views. This part is about displaying individual 'average' votes of your review nodes.
You also need this field to cast it's vote, so individual "averages" count towards overall "average" vote. You could do it in computed field code too, but I think using Rules is more flexible.
Ofcourse you can do like you did before - don't cast average votes at all and calculate it at theming stage of your product node. It's your choice.
My computed field setting:
In computed code
In display format
My Rules rule that acts on updating review looks like this:
UPDATE:
Don't forget, you will also need to clear 'average' axis vote in case review is deleted. So you will need additional Rules action like this:
Comment #15
borfast CreditAttribution: borfast commentedThat's an interesting solution but how about when we need an average for multi-axis ratings that are not provided in a review node - when the rating widgets are present on the rated node itself instead of being CCK fields on a review node?
Is there a 'vote was cast' trigger that can be used with Rules (I don't think so, I didn't see any)? If there isn't, would this be something worth implementing?
Comment #16
crea CreditAttribution: crea commentedDirect Fivestar and Fivestar-CCK are completely different realms. This solution covers only Fivestar-CCK setups. With direct rating, it's probably more simple. Most of complexity in CCK solution comes in the fact rating comes from another node type.
Comment #17
borfast CreditAttribution: borfast commentedThe only possible solution I see (I haven't tried it yet) is to use fivestar_get_votes() along with a fivestar theme function. Am I going in the right direction with this, or is there a simpler way to achieve what I want?
Comment #18
quicksketchborfast, that sounds like exactly the approach I'd probably take. fivestar_get_votes() is currently limited to getting one tag at a time (I think), you might consider going even one level lower and using votingapi_select_votes().
Comment #19
crea CreditAttribution: crea commentedI updated #14 with instructions to clear voting in case review node is deleted.
Comment #20
borfast CreditAttribution: borfast commentedThanks for the tip, quicksketch.
In case this is useful to anyone, I got it working with this code in a function in template.php:
I then print the result of this function wherever I need on my theme.
Far from being the most effective solution but it works...
Thanks again! :)
Comment #21
Jboo CreditAttribution: Jboo commentedHi crea,
This seems like what I need to do, but I'm a bit confused what I need to do exactly. What I'm trying to achieve is to have a 'product' node and a 'review' node. The review node has 3 rating options (design, performance, value). I'm using the node relativity module to get a parent/child type relationship between the product and review nodes. The product node needs to display the average of 'design', 'performance' and 'value' of the all of the review nodes. I have setup each rating with an axis name the same ('design', 'performance', 'value').
I've tried to follow your instructions but I'm unsure whether this is exactly what I'd need to do for my situation, and whether I've actually done it correctly.
This is exactly what I've done so far. In the computed field for the 'product' content type I have this as the computed code:
In the display format:
Rules:
and:
I'd really appreciate it if you could point out whether this is the right approach for what I'm trying to achieve, and what I've done wrong.
Many thanks.
Comment #22
crea CreditAttribution: crea commentedJboo,
You have to use nodereference field for relationship, to use this tutorial! If you don't understand the code, don't use it, find another guy who will do it for you. For noderelativity module you'll have to figure out how to fetch nid of product node from the review node.
Comment #23
davedg629 CreditAttribution: davedg629 commented#14 is a great little tutorial. Wouldn't you need to add a rule that triggers when a "Review" node is created? Otherwise, the average of all the "Overall Ratings" is not recalculated when a new "Review" node is created.
Comment #24
davedg629 CreditAttribution: davedg629 commentedI've been getting some requests to explain how to accomplish this feature. Here is a rough draft of a tutorial:
Tutorial Objective: Create an "Overall Rating" using a multi-axis rating system described here and integrate with Views.
Step 1: Read this tutorial and use it to create a multi-axis rating system with the Fivestar, CCK, Voting API, Computed Fields, Rules, and Views Module.
Step 2: Calculate Overall Rating by averaging each voting axis
Two voting axes were created using the previously mentioned tutorial - reliability and value. The ratings of these two axes will be averaged to create an "Overall Rating". The "Overall Rating" will be calculated in a Computed Field. Add a computed field (make sure you install the Computed Field module first!) to the "Review" content type. Then place the following php code in the "Computed Code" section of the computed field:
This will calculate the average of the two voting axes and store them in a variable that the Computed Field module can display.
Put the following code into the "Display Format" section of the computed field:
This will display the Overall Rating in the fivestar format.
Make sure the "Store using the database settings below" box is checked. Select "float" for the data type and enter "64" for the Data Length (I don't exactly know how big this value needs to be). Make sure the "Sortable" box is checked. Save the field.
Now create a new Review and when the Review is displayed it should automatically calculate the Overall Rating and display it.
Step 3: Use the Rules module to calculate the average of all "Overall Ratings"
Create a new "Triggered Rule" and label it "create_overall_rating". Select "After saving new content" for the Event. Add the "Content has type" condition and select the Product node type under "Content types".
Then add an Action and select "Execute custom PHP code" under Select an action to add. Add the following code under PHP Code:
This will update the average of the "Overall Ratings" every time a new review is submitted. If you want to update the average of the "Overall Ratings" every time a review is updated or deleted, follow comment #14 in this issue
Step 4: Make the "Overall Rating" available to a view displaying the "Product" nodes.
Create a view of the "node" type and add a "Page" display. Make it a "Table" style view and give it the title "Products and their Overall Ratings". Add the node title field to the view and add a node type filter that selects nodes of the content type "Product". Then add a relationship of the type "Node: Voting results". Label it "Overall Rating", select "Percent" for the Value type, select "Vote" for the Vote tag, and select "Average" for the Aggregation function.
Then add a field of the type "Voting API results: Value". Select "Fivestar Stars (display only) for the Appearance and "Overall Rating" for the Relationship. Label the field "Overall Rating" and then save the field.
Then go to the "Table" settings and make the Overall Rating field sortable. You should now have a view that lists all of your Product nodes and their respective Overall Ratings.
Please review and correct if I made any mistakes
Comment #25
ctalley5 CreditAttribution: ctalley5 commentedThanks for putting this together Dave!
Anybody know if this will still apply with the next Fivestar release? Or is it more of a current fix...
Comment #26
thatistosay CreditAttribution: thatistosay commentedOr another way, if you're only concerned about average results for all votes (not just individuals), is to just do this:
Comment #27
arbel CreditAttribution: arbel commentedHow would this part change:
if ($node->status == 1) {
$rating = ($node->field_reliabilty[0]['rating'] + $node->field_value[0]['rating']) / 2;
} else {
$rating = 0;
}
$target = $node->noderef[0]['nid']; //name of the node reference field set up when the review node type was created
_fivestar_cast_vote('node', $target, $rating, 'vote', $node->uid, FALSE, TRUE);
votingapi_recalculate_results('node', $target);
if i'm using node comments module instead of a field reference
Comment #28
friolator CreditAttribution: friolator commented@davedg629:
We're on PHP 5.2.10 and MySQL 5.0.45, and when we use a Float of length 64, we get SQL errors in watchdog:
...when saving the field settings. I don't get this error if the length is 32 -- which I'd think would be more than enough precision for something that's only based on 5 stars!
I don't know if this is a limitation of MySQL or what, but I think in most cases 32 would be enough.
Thanks for the tutorial, by the way - you saved me a ton of time figuring this out on my own!
Comment #29
friolator CreditAttribution: friolator commented@arbel:
we're doing this the same way you are, with Node Comment. Change the code that goes into the Rule so that the $target variable gets the current nodecomment's parent node id. This is in the node object as comment_target_nid. so:
should become:
That should do it.
Comment #30
gulliverrr CreditAttribution: gulliverrr commented@#24 - At Step 3's first line: "Add the "Content has type" condition and select the *Product* node type under "Content types"." should be for *Rating* node type. At least thats what makes sense to me and what makes my example work :)
Thanks a lot Dave for all the info put together!!!
Comment #31
abaddon CreditAttribution: abaddon commentedi have 3 axis which users vote on, the default "vote" one is hidden and its used to calculate totals for the other 3 axis, and im using it in views to sort etc, im sure this is a common setup, im using 2.x-dev and not CCK+extra review node, just basic widget+comments widget to require reviews, comments one seems to modify the node one ok, so all good, and i dont care much if they use the node one to make partial votes (just 1 axis out of 3), just that commenting requires all 3 of them (its actually a photo review site)
regarding #26, im using it with a small modification, if a user just votes on 1 axis, ill get totals like "10.333"... so ive fixed that:
also, see my other 2 posts on fivestar
http://drupal.org/node/786224#comment-2934690 , you need this to be able to hide the "vote" tag widget and let the others axis display, otherwise they all get their settings from "vote" regardless of their own settings, so cant hide "vote" or make it static while the others are clickable, use this patch to make it work
http://drupal.org/node/791414 , you actually need this to make comment voting work
Comment #32
kompressaur CreditAttribution: kompressaur commentedIve got my multi axis voting system almost working thanks to dave's guide and other posts and threads on drupal. Ive got it set up with notecomments and after a straight 60hrs working on it almost it's starting to take shape. I have a few issues but the one thats most puzzling me at the moment is on setting up the noderef selection field. I have it set up via a View with football team names on it. You can view it here
http://onlinebanter.com/dundee-fc
Is there anyway to set it up so as the selection box choses the node contextually? I forgot to mention that i am using panels also. As you can see from that dundee page it will just select the top most team (airdrie) Ive made the mistake myself of adding reviews to the wrong team. Actually a way around it could be just not to place the nodecomment box on a team page but rather a division. anyhow would it be possible to set up the noderef view you think so as it choses the right team?
Also similarly i would love to be able to just put the code that i have put in my node-product.tpl to display the reults into a custom pane but i dont think it was displaying the correct votes. Is this a known issue?
thanks.
Comment #33
kompressaur CreditAttribution: kompressaur commentedAlso would it be possible for me to have 8 or 9 voting axis for each node? I havent seen it mentioned before. someone seemed real proud having 3 axis. am i living beyond my dreams thinking i could have 9 in there? Knowing this might save me a lot of work. thanks.
Comment #34
kompressaur CreditAttribution: kompressaur commentedI just realised 2 things there. 1) the comment form has to be on the page that its commenting on to show up in the view and 2) this thread is all about fivestar 6.2
soz
I'll just have to fix it all in time.
Comment #35
stuartgoff CreditAttribution: stuartgoff commented@14 Shouldn't you delete the DB entry if the 'review' is deleted? If you set to '0' you skew the results.
Comment #36
stuartgoff CreditAttribution: stuartgoff commented@24 & @14 - Went through the process you detailed and worked with no problems. Thanks! I was just wondering if you could drop a step and setup a 5* field on the review for the avg and use rules to set the value.
Comment #37
danadeek CreditAttribution: danadeek commentedThank you davedg629 but how can we display the result in the product node ??? Btw i am using node comment
Comment #38
stuartgoff CreditAttribution: stuartgoff commentedYes, I am having an issue when a node (the set of votes) is deleted the Overall or averaged vote is still in the system. Any help?
Found the item to put in the 'Rule'
votingapi_delete_vote($vobj)
but how do I find the object($vobj)?
Comment #39
TheodorosPloumisI think that this line at the module from http://drupal.org/node/335493#comment-1775512
$vote_avg_count += $data['percent']['count'];
should be:
$vote_points_count += $data['points']['count']; //only points are usefull. Otherwise there will be a zero calculation
and also this line
$cache['vote']['percent']['count'] = $vote_avg_count/$vote_tags;
should be:
$cache['vote']['points']['count'] = $vote_points_count/$vote_tags; //points are used for count
Here is a complete example of the module which calculates for axis vote (overall axis) sum of all other axis points, percent of axis and count of reviews:
Comment #40
wizonesolutionsThe snippet in http://drupal.org/node/335493#comment-1775512 worked well for me. In my case, I changed 'vote' to use a custom tag and related my view to that telling it to filter on "Other and typing in my custom tag name. Worked like a charm and doesn't break the default vote axis.
Comment #41
whiteph CreditAttribution: whiteph commentedThis is the oldest feature request for multi-axis vote averaging, so am going to use this as the "root" and close all of the others as duplicates:
overall rating
A computed aggregate node rating on multi-axis Fivestar fields
Node average rating
Displaying multi-axis average of all votes in Views
Aggregate sub-ratings into an overall star ratings
Display average, average ratings of all nodes referencing the parent on the parent node.
Comment #42
chris_h CreditAttribution: chris_h commentedThis works well as a snippet for a multi-axis average on a referenced node: https://drupal.org/comment/8200647#comment-8200647
Comment #43
whiteph CreditAttribution: whiteph commented@chris_h - thanks, will look at that shortly.