Creating a review system with multi-axis average rating

Last updated on
13 May 2020

Drupal 7 will no longer be supported after January 5, 2025. Learn more and find resources for Drupal 7 sites

This handbook is about how to setup fivestar 7.x-2.0 to use for a review system.
The code examples were initially posted in the issue queue of computed_field, but this should be a more appropriate place.

The code below contains an implementation of fivestar that is used as a review system. It contains some code for your custom module. Replace 'MYMODULE' with the name of your module.

Use case:

Your users rate multiple things (axis) and you also want to show an average rating or other calculated/derived ratings.
This is very typical for a customer-review, where things like 'speed', 'quality' and such are rated and we would like to show an average rating per review.

Summary:

Create an extra fivestar-axis named 'average' on your review, hide it for users and calculate/save it upon saving the review.

Long example with a review content-type.

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.

/**
 * 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;  
  }
}

Long example with a comment instead of a seperate review content-type.

Like above but:

  • 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":

/**
 * 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;
  }
}

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

Help improve this page

Page status: No known problems

You can: