Hi all! Sorry for writing here, but my PHP skills a pretty limited... I got a node type Club: it has four fivestar fields (food, service, price, comfort). I would like to have a fifth fivestar field, lets call it Average Node Rating which is computed as the average of the other four ratings. How do you think I could reproduce it? Is Computed Fields the right module?

Comments

joelrotelli’s picture

Yes, i have done that with Computed field.

I have created a computed_field named "field_moyenne_evaluation" (for the average), and I have surcharged the calculation and display functions by a custom module with this code :

Remember to change the field "field_moyenne_evaluation" by yours !

function computed_field_field_moyenne_evaluation_compute(&$entity_field, $entity_type, $entity, $field, $instance, $langcode, $items)
{   
  
   $field_eval_cuisine = field_view_field('node', $entity, 'field_eval_cuisine');
   $field_eval_service = field_view_field('node', $entity, 'field_eval_service');
   $field_eval_lieu = field_view_field('node', $entity, 'field_eval_lieu');  
   $field_eval_toilettes = field_view_field('node', $entity, 'field_eval_toilettes');
  
   $eval_cuisine = $field_eval_cuisine['#items'][0]['average'];
   $eval_service = $field_eval_service['#items'][0]['average'];
   $eval_lieu = $field_eval_lieu['#items'][0]['average'];
   $eval_toilettes = $field_eval_toilettes['#items'][0]['average'];
  
   $total = $eval_cuisine + $eval_service + $eval_lieu + $eval_toilettes;
  
   $moyenne = $total / 4; 
 
  $entity_field[0]['value'] = $moyenne;    
   
}

function computed_field_field_moyenne_evaluation_display($field, $entity_field_item, $entity_lang, $langcode){
   
    //Récupérer la moyenne des votes du node  
    $moyenne = $entity_field_item['value'];    
   
    $variables = array("rating" => $moyenne, "stars" => 5);
    $variables["widget"]["name"] = "basic";
    $variables["widget"]["css"] = "sites/all/modules/contrib/fivestar/widgets/basic/basic.css"; 

    //Add the fivestar CSS
    drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');

    //Retourner le résultat sous forme d'étoiles
    return theme('fivestar_static', $variables);
   
}

cesareaugusto’s picture

Hi Joel! First thank you really much for replying and sharing! I got a question (I'm not much into the deep Drupal coding thing):

Yes, i have done that with Computed field. I have created a computed_field named "field_moyenne_evaluation" (for the average), and I have surcharged the calculation and display functions by a custom module with this code

I know how to create a computed field... though I don't know how to create a custom module... I found this on the internet http://drupal.org/node/361112... Is it what you are talking about?

joelrotelli’s picture

Hi,

You have to install the Computed Field module, add a Computed field to your content type (mine is field_moyenne_evaluation) and create a Drupal module (with http://drupal.org/node/361112 / the name of your module it's free) and create the two functions I have pasted.

You have to replace the name computed_field_field_moyenne_evaluation_compute and computed_field_field_moyenne_evaluation_display by computed_field_field_YOUR_FIELD_compute

Then you do your calculation in computed_field_field_YOUR_FIELD_compute and save the result in the $entity_field[0]['value'] variable :

For me :

<?php
$entity_field
[0]['value'] = $moyenne;    
?>

In computed_field_field_YOUR_FIELD_display, you get the value previously saved with : $entity_field_item['value'];

For me :

<?php
$moyenne
= $entity_field_item['value'];  
?>

And you send all the variables to the theme function.

Important: it's possible that you have to update your node to apply the changes on your computed field

Hope it help ! I'm french so my english explanation is maybe not very clear...

cesareaugusto’s picture

Thanks again for your help Joel! I'll try it out and post here my results!

I was wondering... What about Views sorting according to such a computed Average Node Rating field? I would like to sort nodes according to their average rating. Would be the average computed on page view? Or stored within the Drupal database?

Again... what about when new users rate contents? Is the computed field updated on the fly?

Merci beaucoup! :)

joelrotelli’s picture

Yes, there is a view support for computed field, so you can sort with the average, I did it, so it works :)

The computed field is updated on the fly when a new user rate content.

Louis Bob’s picture

Hi,

I have a view where I managed to get the averaged numeric value for the nodes => it is the equivalent to the "$moyenne" variable in Joel Rotelli's code.
So for example, I get values such as 67.333, 73.333, etc.
Now I just need to display these values with fivestars, with the stars.
So I used a Global PHP field in the view, in which I pasted the second function of the code (function computed_field_field_moyenne_evaluation_display). I have no error message, but nothing is displayed either. Am I missing a "print" or "echo" something to have the stars displayed correctly ?

descender’s picture

Hi all,

The solution in #1 didn't work for me. For some reason, calling field_view_field() in _compute() crashes PHP with a memory exhaustion error after recursing indefinitely. Another problem is that field_view_field() returns a render array, which is heavy and complex.

What did work, and much simpler for me was to use fivestar_get_votes() like this:

function computed_field_field_overall_score_compute(&$entity_field, $entity_type, $entity, $field, $instance, $langcode, $items) {
  $axis1 = fivestar_get_votes($entity_type, $entity->nid, 'axis1');
  $axis2 = fivestar_get_votes($entity_type, $entity->nid, 'axis2');
  $axis3 = fivestar_get_votes($entity_type, $entity->nid, 'axis3');

  $avg_axis1 = $axis1['average']['value'];
  $avg_axis2 = $axis2['average']['value'];
  $avg_axis3 = $axis3['average']['value'];

  $entity_field[0]['value'] = ($avg_axis1 + $avg_axis2 + $avg_axis3) / 3;
}

axis1, axis2, and axis are the 3 different voting tags I created, each assigned to one of the 3 fivestar fields I have. You have to take care to handle the case where no one has voted yet, as the $axis1['average'] array (for example) will be empty. The snippet above doesn't account for this.

Other things to watch out are:

  • Depending on settings, fivestar may not tally individual ratings until cron is run.
  • Your computed_field should not store the results in the database (there's a setting you can check when creating the field).
jacksboredom’s picture

The second solution by descender worked out for me really well. I used Joel Rotelli's method to manage the displaying of the field and have a question now:

The star-version of the field works but I want to have the numeric value to be displayed too.
How can I manage to do that? What do I have to change in the code?

joelrotelli’s picture

In the fivestar_theme function from the fivestar.module, you can find all the theme formatters you can use to display the value.

I used fivestar_classic (return theme('fivestar_static', $variables);) , but you can try others and send a correct $variables array

jacksboredom’s picture

I'm not a big coding kid. Could you please add a code suggestion for a fitting formatter for displaying the numeric value of the computed average field.

joelrotelli’s picture

I don't know exactly which theme formatter you need, but for exemple, with "fivestar_summary" :

function computed_field_field_moyenne_evaluation_display($field, $entity_field_item, $entity_lang, $langcode){

//Get the average value
$average = $entity_field_item['value'];

$variables = array('average_rating' => $average, 'stars' => 5);

//Add the fivestar CSS
drupal_add_css(drupal_get_path('module', 'fivestar') .'/css/fivestar.css');

//Retourner le résultat sous forme d'étoiles
return theme('fivestar_summary', $variables);

}

You have to format a $variables array corresponding to the array needed in the fivestar_theme function

jacksboredom’s picture

When I use your solution with fivestar_summary, I get back "No Votes" instead of the numeric value.
Any suggestions?

jacksboredom’s picture

?

kapil.ropalekar’s picture

Hi joel rotelli

I followed your exact steps and managed to achieve the average rating from computed field.

My problem is i have set fivestar widget as Stars (rated while editing). So once i enter the rating and save the node only the computed stars are displayed properly and the other ratings show a "No votes yet".

What could i have done wrong. Can anyone help pls

Regards

kapil.ropalekar’s picture

Anybody ???

I figured out that the on node publication the fivestar average values are not getting saved in database.

As of now I have used a simple custom module and used a hook_node_update to update the table by firing a query myself. Working for me :)

glycid’s picture

@ kapil.ropalekar
I've the same problem. Can you post an example of your solution with hook_node_update ?

thanks a lot for your help

ndf’s picture

Working example:

Create 4 voting tags/axis on /admin/config/content/fivestar.

  • vote_axis_one
  • vote_axis_two
  • vote_axis_three
  • votes_average

Create a "review" content-type with 4 voting fields:
to vote on:

  • field_review_vote_axis_one
  • field_review_vote_axis_two
  • field_review_vote_axis_three

calculated:

  • field_review_votes_average.

set your voting targets as you normally do (with a node_reference field).

We hide the average field and calculate it when the review is saved or updated.

<?php
/**
 * Implements hook_form_alter().
 *
 * Hide average fivestar field from review edit/create form.
 */
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  if (
$form_id == 'review_node_form') {
   
// Hide average field, this field is calculated upon node_presave.
   
$form['field_review_votes_average']['#access'] = FALSE;
  }
}

/**
 * Implements hook_node_presave().
 *
 * Calculates average vote rating.
 */
function MYMODULE_node_presave($node) {
  if (
$node->type == 'review') {
   
// Get values from 3 axis.
   
$vote_axis_one      = entity_metadata_wrapper('node', $node)->field_review_vote_axis_one->value();
   
$vote_axis_two      = entity_metadata_wrapper('node', $node)->field_review_vote_axis_two->value();
   
$vote_axis_three    = entity_metadata_wrapper('node', $node)->field_review_vote_axis_three->value();

   
// Calculate and set average axe.
   
$votes_average      = ($vote_axis_one + $vote_axis_two + $vote_axis_three) / 3;
   
// @info: Can't use entity_metadata_wrapper, because '->set' not supported on this field.
    //entity_metadata_wrapper('node', $node)->field_review_votes_average->set($votes_average);
   
$node->field_review_votes_average['und'][0]['rating'] = $votes_average
  }
}
?>

BTW: this code uses entity api (function entity_metadata_wrapper) & fivestar.

irgnet’s picture

How do i change #17 code to work with comments, not with review content type ?

ndf’s picture

A little:

  • Instead of adding the fields to the 'review' contenttype you add as comment fields. At "admin/structure/types/manage/YOUR-CONTENT-TYPE/comment/fields"
  • As voting target you choose "Parent Node".
  • Use the comment form id to hide the field. Thats in the form_alter function. It is probably something like "comment_node_YOUR-CONTENT-TYPE_form".
  • Instead of hook_node_presave use hook_comment_presave. Instead of $node you use $comment and it should work.

In this example the content-type name is "YOUR-CONTENT-TYPE":

<?php
/**
 * Implements hook_form_alter().
 *
 * Hide average fivestar field from review edit/create form.
 */
function MYMODULE_form_alter(&$form, &$form_state, $form_id) {
  if (
$form_id == 'comment_node_YOUR-CONTENT-TYPE_form') {
   
// Hide average field, this field is calculated upon node_presave.
   
$form['field_votes_average']['#access'] = FALSE;
  }
}
/**
 * Implements hook_comment_presave().
 *
 * Calculates average vote rating.
 */
function MYMODULE_comment_presave($comment) {
 
$parent_node = node_load($comment->nid);
  if (
$parent_node->type == 'YOUR-CONTENT-TYPE') {
   
// Get values from 3 axis.
   
$vote_axis_one      = entity_metadata_wrapper('comment', $comment)->field_vote_axis_one->value();
   
$vote_axis_two      = entity_metadata_wrapper('comment', $comment)->field_vote_axis_two->value();
   
$vote_axis_three    = entity_metadata_wrapper('comment', $comment)->field_vote_axis_three->value();
   
// Calculate and set average axe.
   
$votes_average      = ($vote_axis_one + $vote_axis_two + $vote_axis_three) / 3;
   
// @info: Can't use entity_metadata_wrapper, because '->set' not supported on this field.
    //entity_metadata_wrapper('node', $node)->field_review_votes_average->set($votes_average);
   
$comment->field_votes_average['und'][0]['rating'] = $votes_average;
  }
}
?>
irgnet’s picture

Tnanks nielsdefeyter,
A try both with Review and Comments and both works and calulculates average rating.

But firs part of code, which must hide overall rating field
$form['field_review_votes_average']['#access'] = FALSE;
$form['field_votes_average']['#access'] = FALSE;

They both don't work. The field still shows on comment form / node review add form

edg’s picture

Issue summary:View changes

@irgnet You need make sure you have the right $form_id

There's a good video intro to hook_form_alter by Bob at Mustard Seed Media at http://youtu.be/7Wn0sIrGWDI

chris_h’s picture

@nielsdefeyter to check, do your examples use computed field at all?

mariuscmr’s picture

Thank you, @cesareaugusto! Your implementation works great on my website, which has this structure:

Article

  • Review 1 + multi-axis rating + aggregate rating
  • Review 2 + multi-axis rating + aggregate rating
    • Comment 1 + thumbs up/down
    • Comment 2 + thumbs up/down
  • Review 3 + multi-axis rating + aggregate rating

etc.

ndf’s picture

Reply to #22
No, the examples don't use the module 'computed field'. Only entity api & fivestar.

ndf’s picture

Reply to #20
@irgnet: Did #21 solved your problem?

chris_h’s picture

@nielsdefeyter thanks for clarifying. This works perfectly. It's probably worth bringing this to the attention of the Fivestar queue as there's a new maintainer trying to put out a stable release at the moment

ndf’s picture

whiteph’s picture

@nielsdefeyter & chris_h - thanks for your help re FiveStar.

ebenfarnworth’s picture

Thanks for the example in #19. How would you set up the code for views integration? That example works great but views seams unable to pull the displayed data from field_votes_average. When the field is loaded in a view no data is shown, just a blank fivestar field.

NatiNog’s picture

Hi, I used the descender solution at #7 and it worked perfectly. But now I can't sort my view depending on that field.
Can anybody tell my hoe to do that? The field doesn't appear on the list when I try to add a sort criteria.