Blog

2018.11.12

Get WooCommerce customer reviews from all products, display average and all ratings in a histogram without a plugin

I have just completed developing a new customer review feature for Uniqcube light cubes. The idea was to display customer reviews from all products under each product since all products are basically the same product just with a unique design.

I thought that developing this is going to be a walk in the park and I will be done in no time, but as it turned out, WooCommerce does not allow you to do this that easily and you will have to come up with a custom solution.

In this post I would like to share my experience and show you how it can be done without using any plugins.

Why customer reviews matter?

Before we get to the development, let’s dive a little bit into the reviews themselves. User generated content is a force that should not be underestimated. As a study by Brightlocal has shown – 93% of consumers, prior to making a decision of purchase, read online reviews to find out if the product, business is good or bad.

Displaying other customer reviews will not only increase your sales, but also get you higher SEO rankings.

Google uses reviews as one of the components to determine if the website is real or fake therefore if you have this beautiful user generated content under your belt, search engines will reward you with extra traffic over those who do not have them – how cool is that!

Why would anyone want to display all product reviews under a single product?

If you have a store that sells a variety of different products that are hardly related to one another then displaying all product reviews under each product might be confusing. But if you have a series of products that are basically the same with some slight design or material variations then displaying all reviews on one page could be quite useful.

Also you might like to display information about all reviews on your homepage where your visitors will see average rating of all products thus informing them about the overall quality of the store or products it sells.

How to get customer reviews from all products and display them? Update: Pagination included

I will repeat myself, but WooCommerce is the best and most popular e-commerce solution. It is an amazing product for starting your online business. All the functions, hooks and filters it provides to customize your shop is spectacular, but there are some things that you will have to create yourself. So let’s get our hands dirty and dive into the development. Here is an example where I have used these functions to output all reviews.

Update: According to numerous requests I have received after posting this article, I have included review pagination option. It should reduce stress on your servers and decrease page load times. Make sure to change the amount of reviews per page you want to showcase.

Go ahead and open up your functions.php (located in your current themes folder) and add this function to it.

//Display all reviews
if (!function_exists('display_all_reviews')) {
	function display_all_reviews(){
		//Pagination setup
		$reviews_per_page = 10;
		$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
		$offset = ($paged - 1) * $reviews_per_page;		

		$args = array(
			'status' => 'approve',
			'type' => 'review',
			'number' => $reviews_per_page,
			'offset' => $offset
		);

		// The Query
		$comments_query = new WP_Comment_Query;
		$comments = $comments_query->query( $args );

		// Comment Loop
		if ( $comments ) {
			echo "<ol>";
			foreach ( $comments as $comment ): ?>
			<?php if ( $comment->comment_approved == '0' ) : ?>
				<p class="meta waiting-approval-info">
					<em><?php _e( 'Thanks, your review is awaiting approval', 'woocommerce' ); ?></em>
				</p>
				<?php endif;  ?>
				<li itemprop="reviews" itemscope itemtype="http://schema.org/Review" <?php comment_class(); ?> id="li-review-<?php echo $comment->comment_ID; ?>">
					<div id="review-<?php echo $comment->comment_ID; ?>" class="review_container">
						<div class="review-avatar">
							<?php echo get_avatar( $comment->comment_author_email, $size = '50' ); ?>
						</div>
						<div class="review-author">
							<div class="review-author-name" itemprop="author"><?php echo $comment->comment_author; ?></div>
							<div class='star-rating-container'>
								<div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating" class="star-rating" title="<?php echo esc_attr( get_comment_meta( $comment->comment_ID, 'rating', true ) ); ?>">
									<span style="width:<?php echo get_comment_meta( $comment->comment_ID, 'rating', true )*22; ?>px"><span itemprop="ratingValue"><?php echo get_comment_meta( $comment->comment_ID, 'rating', true ); ?></span> <?php _e('out of 5', 'woocommerce'); ?></span>
									
										<?php
											$timestamp = strtotime( $comment->comment_date ); //Changing comment time to timestamp
											$date = date('F d, Y', $timestamp);
										?>
								</div>
								<em class="review-date">
									<time itemprop="datePublished" datetime="<?php echo $comment->comment_date; ?>"><?php echo $date; ?></time>
								</em>
							</div>
						</div>
						<div class="clear"></div>
						<div class="review-text">
							<div itemprop="description" class="description">
								<?php echo $comment->comment_content; ?>
							</div>
							<div class="clear"></div>
						</div>
					<div class="clear"></div>			
				</div>
			</li>

			<?php 
			endforeach;
			echo "</ol>";
			//Pagination output
			echo "<div class='navigation'>";
				$all_approved_product_review_count = get_comments(array(
					'status'   => 'approve',
					'type' => 'review',
					'count' => true
				));
				if( is_float($all_approved_product_review_count / $reviews_per_page) ){
					//In case the value is float, we need to make sure there is an additional page for the final results
					$additional = 1;
				}else{
					$additional = 0;
				}
				$max_num_pages = intval( $all_approved_product_review_count / $reviews_per_page ) + $additional;  
				$current_page = max(1, get_query_var('paged'));
		
				echo paginate_links(array(
					'total' => $max_num_pages
				));
			echo "</div>";
			
		} else {
			echo "This product hasn't been rated yet.";
		}
	}
}

Now we have a function that allows us to display all product reviews on any page of our theme. You can copy this function and paste it in your single product page for example.

<?php echo display_all_reviews(); ?>

If you are going to replace the default WooCommerce customer review output, you also might want to disable review product tab by adding this peace of code to functions.php file.

// Disabling Review Tab on product page
add_filter( 'woocommerce_product_tabs', 'woo_remove_product_tabs', 98 );

function woo_remove_product_tabs( $tabs ) {
	unset( $tabs['reviews'] ); // Remove the reviews tab information tab
	return $tabs;
}

How to get all approved review count from across all products?

After displaying all reviews on a single page, I had to output a number of total reviews I have on my shop. Here is a quick function that will allow you to get this number. Add this to your functions.php file.

//Display all product approved review count in the shop
//Returns comment count
function display_all_approved_product_review_count(){
	return get_comments(array(
		'status'   => 'approve',
		'type' => 'review',
		'count' => true
	));
}

Now ad this peace of code to the template where you would like to display it.

<div class="review-count">
	We have a total of <?php echo display_all_approved_product_review_count(); ?> reviews
</div>

How to get and display average customer rating of all products?

After that I wanted to display average rating and rating in the form of stars. For this we are going to use multiple functions that have to be added to your functions.php file.

/*
 * Retrieving all product review ratings and using WP Transients API to increase query speed
 * Return: Array of all review ratings values ordered from the lowest to highest
 */
function get_all_product_review_ratings(){
    global $wpdb;

    if ( false === ( $review_ratings = get_transient( 'all_product_review_ratings' ))){ //Checking if we have previously cached query results in order to save resources and increase speed
	    $review_ratings = $wpdb->get_results("
	    	SELECT meta_value
	    	FROM {$wpdb->prefix}commentmeta as commentmeta
	    	JOIN {$wpdb->prefix}comments as comments ON comments.comment_id = commentmeta.comment_id
	    	WHERE commentmeta.meta_key = 'rating' AND comments.comment_approved = 1
	    	ORDER BY commentmeta.meta_value
	    ", ARRAY_A);

	    $expiration = 60 * 5; //Expiring query results after 5 minutes
	    set_transient( 'all_product_review_ratings', $review_ratings, $expiration ); //Temporarily storing cached data in the database by giving it a custom name and a timeframe after which it will expire and be deleted

	    return $review_ratings;
	}else{
		return $review_ratings;
	}
}


/*
 * Counting reviews by ratings starting from the highest
 * Return: Array consisting of all rating values and counts how many the rating has been given
 *
 * $minimum_rating: minimal possible product rating
 * $maximum_rating: maximal possible product rating
 */
function get_all_product_review_counts_by_ratings($minimum_rating, $maximum_rating){
	$all_product_review_ratings = get_all_product_review_ratings();
	
	if($all_product_review_ratings){ //If we have reviews
		$all_product_review_ratings_one_dimensional_array = array_map("current", $all_product_review_ratings); //Converting two dimensional array to one dimensional array

		$rating_counts = array_count_values($all_product_review_ratings_one_dimensional_array); //Creating array that consists of rating counts

		$ratings = array();

		while($maximum_rating >= $minimum_rating){
			if(array_key_exists($maximum_rating, $rating_counts)){
				$star_count = $rating_counts[$maximum_rating];
			}else{
				$star_count = 0;
			}

			//Creating array that contains information about 
			$ratings[] = array(
				"value" => $maximum_rating,
				"amount" => $star_count
			);

			$maximum_rating--;
		}
		return $ratings;
	}else{
		return;
	}
}

/*
 * Retrieving average rating of all product reviews and total review count
 * Return: Array of average rating and total review count
 *
 * $minimum_rating: minimal possible product rating
 * $maximum_rating: maximal possible product rating
 */
function get_all_product_review_average_rating($minimum_rating, $maximum_rating){
	$get_all_product_review_counts_by_ratings = get_all_product_review_counts_by_ratings($minimum_rating, $maximum_rating);

	if($get_all_product_review_counts_by_ratings){ //If we have reviews
		$average_rating_results = array();
		$total_ratings = 0;
		$total_rating_value = 0;

		foreach ($get_all_product_review_counts_by_ratings as $key => $rating) {
			$total_ratings = $total_ratings + $rating["amount"];
			$current_rating_value = $rating["amount"] * $rating["value"];
			$total_rating_value = $total_rating_value + $current_rating_value;
		}

		$average_rating = number_format($total_rating_value / $total_ratings, 1); //Rounding value to one decimal place
		$average_rating_results[] = array(
			"total_ratings" => $total_ratings,
			"average_rating" => $average_rating
		);

		return $average_rating_results;
	}else{
		return;
	}
}

/*
 * Creating output of all product review rating
 * Return: String
 *
 * $minimum_rating: minimal possible product rating
 * $maximum_rating: maximal possible product rating
 */
function display_all_product_review_rating($minimum_rating, $maximum_rating){
	if ( get_option( "woocommerce_enable_review_rating" ) === "yes" ){
		$all_product_review_average_rating = get_all_product_review_average_rating($minimum_rating, $maximum_rating);
		if($all_product_review_average_rating){
			$output = '';
			$average = $all_product_review_average_rating[0]["average_rating"];
			$total_ratings = $all_product_review_average_rating[0]["total_ratings"];
		
			$output .= "<div class='star-rating-container'>";
			$output .= wc_get_rating_html( $average, $total_ratings );
			$output .= "<div class='total-ratings'><span class='average rating-number'>". $average ."</span> out of ". $maximum_rating ." stars</div>";
			$output .= "</div>";

			return $output;
		}else{
			return;
		}
	}
}

After that add this line where you would like to output the average rating of all products with stars.

<?php echo display_all_product_review_rating(1, 5);?>

How to display all ratings in a histogram in WooCommerce without a plugin?

And the last thing that was missing for me was a histogram of total customer reviews as one that can be seen on Amazon. This is the function that should go into your functions.php file.

/*
 * Creating output of all product review rating histogram
 * Return: String
 *
 * $minimum_rating: minimal possible product rating
 * $maximum_rating: maximal possible product rating
 */
function display_all_product_review_histogram($minimum_rating, $maximum_rating){
	
	$all_product_review_average_rating = get_all_product_review_average_rating($minimum_rating, $maximum_rating);
	$total_ratings = $all_product_review_average_rating[0]["total_ratings"];

	$get_all_product_review_counts_by_ratings = get_all_product_review_counts_by_ratings($minimum_rating, $maximum_rating);

	if($get_all_product_review_counts_by_ratings){
		$output = '';
		$sum = 0;
		$total = 0;
		$raw_percentages_array = array();
		$percentages_array = array();

		//When working with rounded percentages, we must make sure the total percentages add up to 100%.
		//Creating array of rating values and its percentage
		foreach ($get_all_product_review_counts_by_ratings as $key => $rating) {
			$percentage = round($rating["amount"] / $total_ratings, 2) * 100;
			$raw_percentages_array[] = array("value" => $rating["value"], "percent_of_total" => $percentage);
		}
		//Counting the total of our percents
		foreach($raw_percentages_array as $key => $percent) {
		    $total += $percent[ "percent_of_total" ];
		}
		//Creating an array that will have the actual percentages after the rounding has been applied to it.
		//This will help to see if we have 100% or we are not aligned
	    foreach($raw_percentages_array as $key => $percent){
	        $percentages_array[$percent["value"]] = round(($percent["percent_of_total"]/$total) * 100, 0);
	    }
	    $sum = array_sum($percentages_array); //Again counting the total of our new percents to see if it adds up to 100%

	    if($sum != 100){ //If we do not have 100%, then we will alter the highest percentage value so that we get a total of 100%
	        $highest_percentage_key = array_keys($percentages_array, max($percentages_array)); //Getting key of the highest percentage value
	        $percentages_array[$highest_percentage_key[0]] = 100 - ($sum - max($percentages_array)); //Alterning the percentage
	    }

	    //Now we are ready to create the output that will give us 100% in total
		$output .= "<div class='product-review-histogram'>";
		foreach ($percentages_array as $key => $percentage) {
			$output .= "<div class='histogram-row star-rating-". $key ."'>";
			$output .= "<div class='histogram-col-1'>". $key ." star</div>";
			$output .= "<div class='histogram-col-2'><div class='histogram-meter-bar'><div class='histogram-bar-temperature' style='width: ". $percentage ."%'></div></div></div>";
			$output .= "<div class='histogram-col-3'>". $percentage ."%</div>";
			$output .= "</div>";
		}
		$output .= "</div>";

		return $output;
	}else{
		return;
	}
}

And now you can add this function where you would like to showcase the histogram of all customer reviews.

<?php echo display_all_product_review_histogram(1, 5); ?>

Adding a pinch of CSS

You have now the ability to output all reviews, average review and customer review histogram but in order to make it all look right you will need some bits of CSS code as well. Add these next lines of CSS to your themes style.css file.

/* Star display */
.star-rating {
    overflow: hidden;
    position: relative;
    height: 1.618em;
    line-height: 1.618;
    font-size: 1em;
    width: 5.3em;
    font-family: star;
    font-weight: 400
}

.star-rating::before {
    content: '\53\53\53\53\53';
    opacity: .25;
    float: left;
    top: 0;
    left: 0;
    position: absolute
}

.star-rating span {
    overflow: hidden;
    float: left;
    top: 0;
    left: 0;
    position: absolute;
    padding-top: 1.5em
}

.star-rating span:before {
    content: '\53\53\53\53\53';
    top: 0;
    position: absolute;
    left: 0;
}

p.stars {
    display: inline-block;
    margin: 0;
}

p.stars a {
    position: relative;
    height: 1em;
    width: 1em;
    text-indent: -999em;
    display: inline-block;
    text-decoration: none;
    margin-right: 1px;
    font-weight: 400;
    outline: none;
}

p.stars a:before {
    display: block;
    position: absolute;
    top: 0;
    left: 0;
    width: 1em;
    height: 1em;
    line-height: 1;
    font-family: star;
    content: '\53';
    text-indent: 0;
    opacity: .25
}

p.stars a:hover~a:before {
    content: '\53';
    opacity: .25
}

p.stars:hover a:before {
    content: '\53';
    opacity: 1
}

p.stars.selected a.active:before {
    content: '\53';
    opacity: 1
}

p.stars.selected a.active~a:before {
    content: '\53';
    opacity: .25
}

p.stars.selected a:not(.active):before {
    content: '\53';
    opacity: 1
}

.total-ratings{
	display: inline;
}

.star-rating-container{
	display: block;
	top: -12px;
	position: relative;
}


/* Review Histogram */
.product-review-histogram{
	min-width: 400px;
}
.histogram-row{
	display: block;
	width: 100%;
	margin: 6px 0;
}
.histogram-row:after{
	content: "";
	display: table;
	clear: both;
}
.histogram-col-1,
.histogram-col-2,
.histogram-col-3{
	float: left;
	width: 20%;
	text-align: left;
	font-size: 19px;
	line-height: 26px;
}
.histogram-col-1{
	text-align: right;
}
.histogram-col-2{
	width: 60%;
	padding: 3px 5%;
}
.histogram-meter-bar{
	height: 20px;
	width: 100%;
	background: #454444;
}
.histogram-bar-temperature{
	height: 100%;
	background: #ffa800; /* Old browsers */
	background: -moz-linear-gradient(top, #ffa800 0%, #ef9300 100%); /* FF3.6-15 */
	background: -webkit-linear-gradient(top, #ffa800 0%,#ef9300 100%); /* Chrome10-25,Safari5.1-6 */
	background: linear-gradient(to bottom, #ffa800 0%,#ef9300 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
	filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffa800', endColorstr='#ef9300',GradientType=0 ); /* IE6-9 */
}

The CSS is going to require some additional work on your part to make it aligned with your theme but this will get you going.

Conclusion

Today I have showed you how to get all customer reviews on a single product page. Hopefully it does save you some time and help you get those review improvements ready much faster. Let me know if this helps you or share your code improvements with others below in the comments section.

In categories: Tutorials, Website development, Woocommerce, Wordpress

Nauris Kolāts

Nauris is a freelance designer / developer who loves to dig into the UX as much as in the ground for fishing worms. And fishing is just one amongst the long list of his active lifestyle hobbies.

Other posts

Your thoughts

Join the discussion

  1. Monirul Islam

    Awesome design!

  2. chris

    Hi, this is what i need to achive however it doesnt function with Woocommerce variable subscription products, how can i make it work with these products?

    • Hi Chris,

      Thanks for your comment.
      This tutorial was intended to help others in the same position to display review histogram. To my understanding a variation is basically the same product so it doesn’t really make a lot of sense to display separate reviews for different variations.
      I’m guessing you are still selling one subscription product with some slight variations – to your customers that might look like the same product after all.

  3. asghar

    hi

    How can I use slides instead of stars?
    I want to use slide when reviewing reviews

    Thank you so much for your help

    • Hi Asghar, could you please show an example of what you mean when referring to “slides instead of stars”?

  4. tamakichi

    great customize!
    I want to add pagenation of customer reviews from all products. any ideas?

    • Hi Tamakichi, I have updated the tutorial so that now reviews can be also split into pages.

  5. zan

    Hello. Great article. I would just like to ask you how can I display comments from other product in the same category. So If I have 3 product in category shirts the product rewiev for all 3 product would be displayed for all products in the same category.

    Thanks for your help.

    • Hi Zan,

      You will have to add additional args to the WP_Comment_Query function.
      So you would have to call it something like this:

      $args = array(
      'status' => 'approve',
      'type' => 'review',
      'post_id' => 123 //Here you must replace 123 with the product's ID.
      );
      // The Query
      $comments_query = new WP_Comment_Query;
      $comments = $comments_query->query( $args );

      This way you have added a specific post_id value in the WP_Comment_Query making it ask for just one specific product.
      If you would like to include a group of products, you would be replacing “post_id” with “post__in => array( 123, 234, 456 );” where you replace the numbers with your product ID values.
      This WP_Comment_Query function is quite flexible and offers a lot of different options, you can also restrict reviews by review author, you can use database queries and meta queries.

      You can read more about the WP_Comment_Query here: https://developer.wordpress.org/reference/classes/wp_comment_query/

  6. yomo

    Hi team,

    how to create pagination?

    • Hey Yomo, I have updated the tutorial so that it uses pagination and allows to paginate reviews.

  7. seven

    Hello, how are you? thanks for sharing. I am a newbie for woocommerce, can you tell me how to use PHP code in a page? like

  8. Marcos

    Hi Nauris! You can’t imagine for how long I have looked for this solution! I have similar products and I wanted to display ALL reviews on each single product.

    I only encountered a problem: I cannot seem to display the customer name and comment BUT only my name and my reply.

    Also, after adding the CSS code I can see that stars from 1 to 9 are greyed out and only review number 10 shows 5 stars in black AND also customer comment! Why is this happening?

    Screenshot of problem: https://imgur.com/lPNL6kf

    Thanks a lot! Stay safe.

    • Hey Marcos, glad to hear this :)
      Not really sure why you are not able to display the name and comment besides each review.

      I would suggest you to go one step at a time and try debugging using PHP var_dump() function to see what output you are getting from different variables. Start by trying to see what you are getting with this e.g. var_dump($comment->comment_author)

      And the issue with grayed out stars – most probably it is just a CSS conflict with some other CSS classes. I would suggest you to switch briefly to a default WordPress template and see if that changes anything both in the way stars look and how comment related data is outputted.

      Best wishes and you too stay healthy and safe! <3

  9. Pedro

    Hey Nauris,
    really great article, thank you!
    I want to show the reviews/ratings of all my products in the woocommerce shop on each product single page.
    Do you think, Google will penalty this or lock the rich snippets?
    Background: Since some time Google does no longer show the reviews/rating stars of the shop on the single product pages. Only the reviews/ratings of the single product are shown in Google SERP Rich Snippet.
    Greets – Pedro

    • Hi Pedro, thanks for your question however it’s a tricky one since Google doesn’t really give out a lot about their search algorithms and how they rank the websites. I do not believe they should be penalizing website for outputting all reviews on all single product pages since your reviews might symbolize and sum up the overall experience and quality of service you provide.

  10. Bas

    Hi Nauris, is it possible to use your code in combination with the standard woocommerce reviews tab (the one which is regular used at WooCommerce product pages? So that all products have all reviews under this standard tab and there’s no need to place the reviews at another spot on the product page? Thanks!

    • Hi Bas, I haven’t tested if it is possible to display them instead of default WooCommerce reviews.
      I guess it should be possible with some hooks or filters however it would take a bit of additional custom development :)

  11. Kristjan

    Outstanding work Nauris. Thanks alot, this helped me. Is it possible to make pagination ajax loading with your current solution?

    • Hey Kristjan, thanks for your feedback. I’m afraid that adding Ajax is going to out of the scope of this tutorial :) It can be done, but at the moment I’m a bit tangled up in some of my projects so not really able to dive back here. Sorry for this.

  12. Eva last

    Hi, thx for providing the great way to customize the woo reviews section. After adding it to my site i realized it shows the global reviews… Any way to show only the reviews of the current single product? THX

  13. Eva last

    Hi again – i was not able to get the expected results so i changed the structure of my reviews. Now i only need the histogram for a single product where i can show a overview of the approved reviews for the particular product. Any help would be appreciated. THX

    • Hi, glad to hear you are moving ahead! :)
      I would love to help out, but at this point I’m consumed by other ongoing projects. I’m afraid this is something you are going to figure out yourself. And once you do, it would be awesome if you could share your results here with the rest of us.

  14. Alex

    How to get and display product reviews on any page from certain product_category, product_tag ore custom taxonomy?
    Thanks.

  15. jj

    This is really great! I have 2 quick questions.
    1. Why don’t you use wp_reset_postdata()?
    2. Can you make it pull 10 random reviews? So every time the page with this function loads it’s a different set of reviews?

    • Hi Josh,
      1) You could add the wp_reset_postdata() function after the reviews. It will be required in case you are doing additional WP Queries after this review block.
      2) And from looking at the WordPress official documentation, it does not seem like you can display comments in a random order is it can be done with WP_Query using 'orderby' => 'rand'

Latest work