WordPress

How to Add a Rate Field to WordPress Comments

In this article I’ll show you how to add a rating field to your WordPress comments. We’ll work with PHP to enable the new field and show it in the comments template. Also, we’re going to do some styling in order to give a nice look to the rate system. Finally we’ll also create a small JavaScript using jQuery to enable it.

Introduction

In this article we’ll create the rating system in four steps:

  • Add the comment meta data
  • Update the comments template
  • Add new CSS styles
  • Add new JavaScript
  • How to Get the Rating Average (update)

So, let’s begin.

Comment Meta Data

The first thing we need to do is to open our functions.php file and add the following code:

add_action('comment_post','comment_ratings');

function comment_ratings($comment_id) {
    add_comment_meta($comment_id, 'rate', $_POST['rate']);
}

As you can see it’s pretty simple. We’re adding a action to be executed when the comment is posted. Inside the function comment_ratings, we’re adding the new information to the comment meta data. In this case I’m adding the field rate with the value of the field rate that was posted.

Now we need to add the field rate to the comment template.

Comments Template

We need to add the field in two places: where we display the comments and where the visitor enter his comment. As we already have the functions.php file opened, let’s add the following function (it’s the same function from the WordPress Codex, with the additional rate field):

function comment_template($comment, $args, $depth) {
    $GLOBALS['comment'] = $comment; ?>
    <li <?php comment_class(); ?> id="li-comment-<?php comment_ID() ?>">
        <div id="comment-<?php comment_ID(); ?>">
            <div class="comment-author vcard">
                <?php echo get_avatar($comment,$size='48'); ?>

                <?php printf(__('<cite class="fn">%s</cite> <span class="says">says:</span>'), get_comment_author_link()) ?>
            </div>

            <?php if ($comment->comment_approved == '0') : ?>
            <em><?php _e('Your comment is awaiting moderation.') ?></em>
            <br />
            <?php endif; ?>

            <div class="comment-meta commentmetadata"><a href="<?php echo htmlspecialchars( get_comment_link( $comment->comment_ID ) ) ?>"><?php printf(__('%1$s at %2$s'), get_comment_date(),  get_comment_time()) ?></a><?php edit_comment_link(__('(Edit)'),'  ','') ?></div>

            <?php
            $rate = get_comment_meta($comment->comment_ID, 'rate');
            if ($rate[0] <> '') {
                _e('Grade: ');
                echo movie_grade($rate[0]);
            }
            ?>

            <?php comment_text() ?>

            <div class="reply">
                <?php comment_reply_link(array_merge( $args, array('depth' => $depth, 'max_depth' => $args['max_depth']))) ?>
            </div>
        </div>
<?php
}

This function will be used to display the comments list. We added here a call to the get_comment_meta function. It will return an array with the rate information. If it’s empty, we’ll not display anything. If it’s not, then we’ll show it using the following function:

function movie_grade($grade) {
    switch ($grade) {
        case '0':
            $alt = 'Zero - 0 stars';
            break;
        case '1':
            $alt = 'Really bad - 1 star';
            break;
        case '2':
            $alt = 'Bad - 2 stars';
            break;
        case '3':
            $alt = 'Good - 3 stars';
            break;
        case '4':
            $alt = 'Very good - 4 stars';
            break;
        case '5':
            $alt = 'Excellent - 5 stars';
            break;
        default:
            $alt = 'No grade';
            break;
    }

    if (!isset($grade) || $grade == '')
        echo $alt;
    else {
        for ($i = 0; $i < 5; $i++) {
            if ($grade > $i)
                echo '<img src="'.get_stylesheet_directory_uri().'/images/star_on.png" alt="'.$alt.'" title="'.$alt.'" />';
            else
                echo '<img src="'.get_stylesheet_directory_uri().'/images/star_off.png" alt="'.$alt.'" title="'.$alt.'" />';
        }
    }
}

In this function we’re displaying images according to the rate posted by the visitors. Last thing we need to do in order to show the rates in the comments list is in theΒ comments.php file. Find the call to wp_list_comments and add the parameter ‘type=comment&callback=comment_template’. It should look like this:

<?php wp_list_comments('type=comment&callback=comment_template'); ?>

This will display the comments list using the callback function comment_template that we created earlier. The next and last thing we need for the comments template is it to update the comment entry. We need to add a rate field there. Let’s add some image links, to make it more functional.

<div>
    <div class="comment-rating">
        <ul class="star-rating">
            <li><a href="#" title="<?php _e('Zero - 0 stars'); ?>" class="zero-star" onclick="rateClick(0); return false;">0</a></li>
            <li><a href="#" title="<?php _e('Really bad - 1 star'); ?>" class="one-star" onclick="rateClick(1); return false;">1</a></li>
            <li><a href="#" title="<?php _e('Bad - 2 stars'); ?>" class="two-stars" onclick="rateClick(2); return false;">2</a></li>
            <li><a href="#" title="<?php _e('Good - 3 stars'); ?>" class="three-stars" onclick="rateClick(3); return false;">3</a></li>
            <li><a href="#" title="<?php _e('Very good - 4 stars'); ?>" class="four-stars" onclick="rateClick(4); return false;">4</a></li>
            <li><a href="#" title="<?php _e('Excellent - 5 stars'); ?>" class="five-stars" onclick="rateClick(5); return false;">5</a></li>
        </ul>
    </div>
    <?php _e('Your rate'); ?>
    <input type="hidden" name="rate" id="rate" value="<?php echo esc_attr($comment_author_rate); ?>" />
</div>

Next we’ll style the links and add JavaScript to update the hidden input.

New CSS Styles

We’ll create the styles we’ve used in the HTML. These styles will replace the links with images and use a hover effect. I pretty much copied it from Create a Star Rater using CSS. So, the necessary CSS is:

.comment-rating {float:left; width:180px;}
.star-rating,
.star-rating a:hover,
.star-rating a:active,
.star-rating a:focus,
.star-rating .current-rating{background: url(images/star.png) left -1000px repeat-x;}
.star-rating{position:relative;width:108px;height:25px;overflow:hidden;list-style:none;margin:0;padding:0;
             background-position: left top;}
.star-rating li{display: inline;}
.star-rating a,
.star-rating .current-rating{position:absolute;top:0;left:0;text-indent:-1000em;height:25px;line-height:25px;
                             outline:none;overflow:hidden;border: none;}
.star-rating a:hover,
.star-rating a:active,
.star-rating a:focus{background-position: left bottom;}
.star-rating a.one-star{width:34%;z-index:6;}
.star-rating a.two-stars{width:51%;z-index:5;}
.star-rating a.three-stars{width:68%;z-index:4;}
.star-rating a.four-stars{width:85%;z-index:3;}
.star-rating a.five-stars{width:100%;z-index:2;}
.star-rating .current-rating{z-index:1;background-position: left center;}
.star-rating a.zero-star {width:17%;z-index:8;background: url(images/no_star.png) left top no-repeat;}
.star-rating a.zero-star:hover,
.star-rating a.zero-star:active,
.star-rating a.zero-star:focus {background-position: left center;}
.star-rating a.zero-selected {background-position: left center;}

We defined here a width of 180px for the hole DIV. It’s a quick attempt to align it with the other texts. In Chrome it looks OK, but not in Firefox. You may choose an alternative way to align them. Anyway, you’ll need the following image files:

It was possible to use the same files, but I was lazy to update the hole thing… So, after we had created the functions, updated the templates and created the CSS, now it’s time to create the JavaScript.

New JavaScript

We’re going to use jQuery functions, so you’ll need to have it enqueued in your blog. In our js file we have two functions. They may not be optimized, but it works πŸ˜‰

function clearSelected() {
    $('.star-rating a.zero-star').removeClass('zero-selected');
    $('.star-rating a.one-star').removeClass('current-rating');
    $('.star-rating a.two-stars').removeClass('current-rating');
    $('.star-rating a.three-stars').removeClass('current-rating');
    $('.star-rating a.four-stars').removeClass('current-rating');
    $('.star-rating a.five-stars').removeClass('current-rating');
}

function rateClick(num) {
    clearSelected();
    switch (num) {
        case 0:
            $('.star-rating a.zero-star').addClass('zero-selected');
            $('#rate').val('0');
            break;
        case 1:
            $('.star-rating a.one-star').addClass('current-rating');
            $('#rate').val('1');
            break;
        case 2:
            $('.star-rating a.two-stars').addClass('current-rating');
            $('#rate').val('2');
            break;
        case 3:
            $('.star-rating a.three-stars').addClass('current-rating');
            $('#rate').val('3');
            break;
        case 4:
            $('.star-rating a.four-stars').addClass('current-rating');
            $('#rate').val('4');
            break;
        case 5:
            $('.star-rating a.five-stars').addClass('current-rating');
            $('#rate').val('5');
            break;
    }
}

In the first function we only clear the selected classes. In the second one we add the selected class and set the hidden input with the correct value.

How to Get the Rating Average

This section is an update to this post. Updated in July 27th, 2011.

It is quite easy to calculate the average of ratings posted in your comments. First, we need to add the following function inside our functions.php file:

function get_average_ratings($id) {
    $comment_array = get_approved_comments($id);
    $count = 1;

    if ($comment_array) {
        $i = 0;
        $total = 0;
        foreach($comment_array as $comment){
            $rate = get_comment_meta($comment->comment_ID, 'rate');
            if(isset($rate[0]) && $rate[0] !== '') {
                $i++;
                $total += $rate[0];
            }
        }

        if($i == 0)
            return false;
        else
            return round($total/$i);
    } else {
        return false;
    }
}

This function will get all approved comments for a given post ID and loop through them summing up the total value of the rate field. It will sum up only when the rate field was defined. So, if the user didn’t defined a rate, it will not be calculated. This function will return the average of ratings or will return false if there are no approved comments.
Later you can call this function in your single.php file. You can use it to first verify if there are ratings. If there are ratings, you can use the function movie_grade() to display it properly. The code to use it could be something like this:

<?php (get_average_ratings($post->ID))?movie_grade(get_average_ratings($post->ID)):'Not available'; ?>

Conclusion

That’s all I had to show this time. It’s possible to use similar functions to create any comment entry template. This way you could not only make your blog unique, but also make the discussion unique. Here is a preview of the result (more or less):

Rate Comment Preview

Rate Comment Preview


  • Stephan

    Hi,

    This was something I have been looking for. I installed it all but I have 1 little problems and wonder if you could help me with them.

    When i click on a star it calls the function clearSelected but I get an error when calling that function. The error is “Error: $ is not a function”

    Can you help???

    Thanks

  • Stephan

    No worries. I have solved the problem. I it was a conflict between libraries. Used this site http://devoracles.com/jquery-error-documentready-is-not-a-function-sure to find the solution.

  • Thanks for this awesome tutorial. I don’t want to create a star rating system but I wanted to know how to add extra fields to the comment template. There is hardly any content out there pointing to this direction. Hence, great job πŸ™‚

    • One more doubt though, if we add a text field, we would have to sanitize it in the comments_rating method, while using the $_POST version of it, right?

      • Hi Sumeet,
        You’re right, the input needs to be sanitized in order to prevent malicious entries. The add_metadata function will do stripslashes, but you need to check what kind of entry you have and what else you need…

  • Hi Oscar,

    Thanks for this great tutorial.

    I’ve installed but unfortunately it isn’t working. For some reason the comment meta isn’t getting stored in the db.

    If you have time, can you take a look?

    http://www.boulderfringe.com/programs/2010/dance/canyon-movement-company-inc/

    any help would be much appreciated!

    Thanks,
    Brandon

    • Hey, I borrowed some of your code and changed some to make it work. I like the way it turned out.

      Thanks!

      You can see it at the previously posted link.

  • Thanks a lot. Your blog really helped me.

  • Michelle

    I’ve tested the tutorial on my local machine, it is working except for the javascript. Thank you for giving me an idea on how to use the comment meta feature of wordpress.

  • RJ

    How would I display the average of all comments/reviews on top of the post

    • Hi RJ, just updated the post with this information. Please check it out.

  • I have been looking for such tutorial from a few weeks. I am over joyed to see this today. But I have a question that how can I implement multiple ratings ….

  • sol

    Holla friends.

    Version 2 of my WP Extra Comment Fields plugin is now available.
    http://www.solaceten.info/wp-extra-comment-fields-v2

    Now fully automated installation and compliant with WP standards πŸ™‚

    You can add extra comment fields automatically using the plugin,

    – make them public or private
    – and make them mandatory or not
    – receive them in email admin notification
    – Use radio, text fields or dropdowns!

    Lots of options πŸ™‚
    Lots of additional features planned πŸ™‚

    http://www.solaceten.info/wp-extra-comment-fields-v2

    Regards
    Sol

  • thanks

  • How to make half star rating. e.g. : 2.5 stars, 3.5 stars, etc? Thanks before πŸ™‚

    • Well, you need to update the code in different places to support half stars. You need to update the function movie_grade adding the new values into the switch statement and add another if statement for the half star in the for loop (something like “if ($grade > ($i+0.5))”). The next step is to update the ul.star-rating with additional links and classes. These classes need to be created in the CSS as well. Finally, you need to update the JavaScript functions as well to work with your half star (update the switch statement).

    • I also needed 0.5 stars. I’ve edited the various functions etc to suit my needs.. I changed a lot of the code so you’ll need to edit it for your needs. The logic though should help or at the least give you inspiration (sorry for length of comment)

      function show_rating($rating) {
      switch ($rating) {
      case ‘0.5’:
      $alt = ‘Really bad – 0.5 stars’;
      break;
      case ‘1’:
      $alt = ‘Really bad – 1 star’;
      break;
      case ‘1.5’:
      $alt = ‘Bad – 1.5 stars’;
      break;
      case ‘2’:
      $alt = ‘Bad – 2 stars’;
      break;
      case ‘2.5’:
      $alt = ‘Good – 2.5 stars’;
      break;
      case ‘3’:
      $alt = ‘Good – 3 stars’;
      break;
      case ‘3.5’:
      $alt = ‘Very good – 3.5 stars’;
      break;
      case ‘4’:
      $alt = ‘Very good – 4 stars’;
      break;
      case ‘4.5’:
      $alt = ‘Excellent – 4.5 stars’;
      break;
      case ‘5’:
      $alt = ‘Excellent – 5 stars’;
      break;
      default:
      $alt = ‘No grade’;
      break;
      }

      if (!isset($rating) || $rating == ”)
      echo $alt;
      else {
      $full = floor($rating);
      $remainder = fmod($rating, 5);
      $empty = 5 – $full;
      for ($i = 0; $i < $full; $i++) {
      echo '’;
      }
      if($remainder == 0.5) {
      echo ”;
      }
      for ($i = 0; $i < $empty; $i++) {
      echo '’;
      }
      }
      }

      function get_average_ratings($id) {
      $comment_array = get_approved_comments($id);
      $count = 1;

      if ($comment_array) {
      $i = 0;
      $total = 0;
      foreach($comment_array as $comment){
      $rating = get_comment_meta($comment->comment_ID, ‘rating’);
      if(isset($rating[0]) && $rating[0] !== ”) {
      $i++;
      $total += $rating[0];
      }
      }

      if($i == 0)
      return false;
      else
      return round_to_half($total/$i);
      } else {
      return false;
      }
      }

      function round_to_half($num) {
      if($num >= ($half = ($ceil = ceil($num))- 0.5) + 0.25) return $ceil;
      else if($num < $half – 0.25) return floor($num);
      else return $half;
      }

      HTML…

      <!– <a href="#" title="” class=”zero-star” onclick=”rateClick(0); return false;”>0 –>
      <a href="#" title="” class=”star-05″ rel=”0.5″ onclick=”rateClick(0.5); return false;”>0.5
      <a href="#" title="” class=”star-10″ rel=”1.0″ onclick=”rateClick(1); return false;”>1
      <a href="#" title="” class=”star-15″ rel=”1.5″ onclick=”rateClick(1.5); return false;”>1.5
      <a href="#" title="” class=”star-20″ rel=”2.0″ onclick=”rateClick(2); return false;”>2
      <a href="#" title="” class=”star-25″ rel=”2.5″ onclick=”rateClick(2.5); return false;”>2.5
      <a href="#" title="” class=”star-30″ rel=”3.0″ onclick=”rateClick(3); return false;”>3
      <a href="#" title="” class=”star-35″ rel=”3.5″ onclick=”rateClick(3.5); return false;”>3.5
      <a href="#" title="” class=”star-40″ rel=”4.0″ onclick=”rateClick(4); return false;”>4
      <a href="#" title="” class=”star-45″ rel=”4.5″ onclick=”rateClick(4.5); return false;”>4.5
      <a href="#" title="” class=”star-50″ rel=”5.0″ onclick=”rateClick(5); return false;”>5

      notice with #rating-hover-score. I’ve got one of those ‘3 out of 5’ things that change as you hover. Javascript for that…

      jQuery(document).ready(function($){
      $(‘.star-rating a’).each(function(){
      var value = $(this).attr(‘rel’);
      $(this).hover(function(){
      $(‘#rating-hover-score’).html(value + ‘ out of 5’);
      });
      });

      $(‘.star-rating’).mouseleave(function(){
      $(‘#rating-hover-score’).html(”);
      });
      });

      I also changed the clearSelected() function.

      function clearSelected() {
      $(‘.star-rating a’).removeClass(‘current-rating’);
      }

      You’ll also need to change the rateClick javascript function but it should be obvious what you need to do

      Lastly you’ll need to edit the widths in the css. Also have a half star graphic.

      span.star { display: block; height: 18px; width: 18px; background-image: url(images/stars.png); float: left; }
      span.star_off { background-position: center top; }
      span.star_on { background-position: center bottom; }
      span.star_half { background-position: center center; }

      • correction.

        fmod($rating, 5) should be fmod($rating, 1)

      • That’s awesome! Thanks for sharing!

      • Can’t wait to try it πŸ™‚ Thank you very much.

  • Where should i add this line ?

    and this

    <a href="#" title="” class=”zero-star” onclick=”rateClick(0); return false;”>0
    <a href="#" title="” class=”one-star” onclick=”rateClick(1); return false;”>1
    <a href="#" title="” class=”two-stars” onclick=”rateClick(2); return false;”>2
    <a href="#" title="” class=”three-stars” onclick=”rateClick(3); return false;”>3
    <a href="#" title="” class=”four-stars” onclick=”rateClick(4); return false;”>4
    <a href="#" title="” class=”five-stars” onclick=”rateClick(5); return false;”>5

    <input type="hidden" name="rate" id="rate" value="” />

    • Sorry, I didn’t got all your message because you added tags in it. Anyway, the links with the rate images should go in your theme comments.php file, just before the message textarea.

  • Amber

    Thanks – this saved me a lot of time writing from scratch. I was looking for a plugin but they are all bloated or geared for different things. Really appreciate you taking the time to publish πŸ™‚

  • Sam

    Hey I have everything correct, but the
    if ($rate[0] ”) {
    _e(‘Grade: ‘);
    echo movie_grade($rate[0]);
    }
    ?>

    Is not showing the user star rate/Grade on their output. Is like is not echoing anything.

    Please advice
    Sam

  • SalemMed Haroun

    Awesome !! Thank you !!

  • Paolo

    Hi, if I want to assigne the rate to a different taxonomy then post page? I have one called “resume”. Can i use that?

  • alamin123

    i don’t use this code please help me