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