Display Product rating in the products list in Prestashop

Regardless of what we sell, it’s always a good idea to showcase how much our products are loved by our customers. Prestashop comes with a review module, but the rating only displays in the product page. Let’s see how to show the product rating in the products list too!

  • Version used: Prestashop 1.5.4
  • Product Comments Module: version 2.3

Introduction

It’s vital for every shop to let customers know about the experience of people who previously bought there. It’s the essence of e-commerce, and people tend to trust more a shop whose customers review those products they bought, If they’re happy with them. Thus, it’s becoming more and more important to have some kind of review system.
Prestashop comes with the so-known “Prestashop Product comments” module, which adds a basic review system to our store. The problem with this is that it’s only displaying reviews (and, subsequently, ratings) in the product detail page. In this tutorial, we will modify the existing module to allow it to hook to product listings, thus enabling each page displaying products to also have those nice stars displayed. Here is the result we want to achieve:

Prestashop product rating in Products List - End result

Step 1 – Adding a new hook to the Product Rating module

In order to display those cute stars in the product list, we need a new hook. This hook will be placed right below the product name in the list, but to begin with, let’s open up modules/productcomments/productcomments.php, and find the install() method.

	public function install()
	{
		if (!file_exists(dirname(__FILE__).'/'.self::INSTALL_SQL_FILE))
			return false;
		else if (!$sql = file_get_contents(dirname(__FILE__).'/'.self::INSTALL_SQL_FILE))
			return false;
		$sql = str_replace(array('PREFIX_', 'ENGINE_TYPE'), array(_DB_PREFIX_, _MYSQL_ENGINE_), $sql);
		$sql = preg_split("/;\s*[\r\n]+/", trim($sql));

		foreach ($sql as $query)
			if (!Db::getInstance()->execute(trim($query)))
				return false;
		if (parent::install() == false ||
			!$this->registerHook('productTab') ||
			!$this->registerHook('extraProductComparison') ||
			!$this->registerHook('productTabContent') ||
			!$this->registerHook('header') ||
			!$this->registerHook('productOutOfStock') ||
			!Configuration::updateValue('PRODUCT_COMMENTS_MINIMAL_TIME', 30) ||
			!Configuration::updateValue('PRODUCT_COMMENTS_ALLOW_GUESTS', 0) ||
			!Configuration::updateValue('PRODUCT_COMMENTS_MODERATE', 1))
				return false;
		return true;
	}

We want to add our new hook there, so let’s do it right after registering the productOutOfStock hook:

	public function install()
	{
		if (!file_exists(dirname(__FILE__).'/'.self::INSTALL_SQL_FILE))
			return false;
		else if (!$sql = file_get_contents(dirname(__FILE__).'/'.self::INSTALL_SQL_FILE))
			return false;
		$sql = str_replace(array('PREFIX_', 'ENGINE_TYPE'), array(_DB_PREFIX_, _MYSQL_ENGINE_), $sql);
		$sql = preg_split("/;\s*[\r\n]+/", trim($sql));

		foreach ($sql as $query)
			if (!Db::getInstance()->execute(trim($query)))
				return false;
		if (parent::install() == false ||
			!$this->registerHook('productTab') ||
			!$this->registerHook('extraProductComparison') ||
			!$this->registerHook('productTabContent') ||
			!$this->registerHook('header') ||
			!$this->registerHook('productOutOfStock') ||
			!$this->registerHook('productInList') ||
			!Configuration::updateValue('PRODUCT_COMMENTS_MINIMAL_TIME', 30) ||
			!Configuration::updateValue('PRODUCT_COMMENTS_ALLOW_GUESTS', 0) ||
			!Configuration::updateValue('PRODUCT_COMMENTS_MODERATE', 1))
				return false;
		return true;
	}

As you noticed, we adedd !$this->registerHook(‘productInList’) ||. This will be the name of our new hook.
Reset or install the module, then place a couple of comments and give them some different star rating. be sure they appear in the product page.

Step 2 – Adding the hook to the product list template

Open up product-list.tpl, which can be found in themes/nameofyourtheme folder. I will be using the default 1.5.4 Template for this example.

Locate the product name, which should be around line 44:

	<h3><a href="{$product.link|escape:'htmlall':'UTF-8'}" title="{$product.name|escape:'htmlall':'UTF-8'}">{$product.name|escape:'htmlall':'UTF-8'|truncate:35:'...'}</a></h3>

After it, add the following snippet:

	{hook h='productInList' idproduct=$product.id_product}

Explanation: we are taking advantage of a new way to add hooks, which was introduced in Prestashop 1.5 (To learn more: Adding new hooks to prestashop (the new way)). We can pass any number of arguments to the parameters of the hook, and in this case I chose to pass the product id, which is available to us since we are right inside the products iteration function (foreach loop).

Step 3 – Creating the hooking function

So, we have our hook. Let’s test it out. At the end of productcomments.php create a new function, as such:

	function hookProductInList($params)
	{
		echo 'hey there';
	}

Save & refresh, and you should get this:

Prestashop product rating in Products List - New hook in action

If you can’t see it, go back and check the previous steps, as it means the hook is not being processed. if it is, move forward and change the lastly created function this way:

	function hookProductInList($params)
	{
		require_once(dirname(__FILE__).'/ProductComment.php');
		require_once(dirname(__FILE__).'/ProductCommentCriterion.php');

		$id_product = $params['idproduct'];
		$product_average = 0;

		$grades = ProductComment::getAveragesByProduct($id_product, $this->context->language->id);
		$criterions = ProductCommentCriterion::getByProduct($id_product, $this->context->language->id);
		$grade_total = 0;
	}

Explanation: First, we need the ProductComment and ProductCommentCriterion classes, and we include them at the beginning of the method. Then, we define the product id as a variable, to avoid accessing the huge params array every time. Lastly, we get all the average grades for the product and all the criterions, and set a default value for the total grade, to avoid undeclared variable issues. Let’s go ahead and calculate the average grade for every product.

After the previous code, add the following:

		if (count($grades) > 0)
		{
			foreach ($criterions as $criterion)
			{
				if (isset($grades[$criterion['id_product_comment_criterion']]))
				{
					$grade_total += (float)($grades[$criterion['id_product_comment_criterion']]);
				}
			}

			$product_average = $grade_total / count($criterions);
		}

Explanation: We check that we actually have grades, and then, for each criterion, if the grades array contains something for that criterion, we add it to the total. In this stage, we are building the sum of all grades. After looping through all the criterions, we get the total average dividing the total grade by the number of criterions. This way we are sure the final average is the “average of all averages” of all criterions.

As a final step in this function, let’s assign the number we just got, and return a template:


		$this->context->smarty->assign('average_total', (int)$product_average);
		return $this->display(__FILE__, '/product-list.tpl');

We want to get the integer part of the average so be sure to add (int) when assigning it. We don’t have the mentioned product-list.tpl template file in the module’s folder, let’s create it in the last step!

Step 4 – Creating a template file to display the rating

Create a new .tpl file in modules/productcomments and call it product-list.tpl. Be sure it has the .tpl extension. if you don’t know how to create one from scratch, copy and rename one of the files which are in the folder, and delete everything it contains. Open up the new file and add the following:

	<div class="star_content clearfix">
	{section name="i" start=0 loop=5 step=1}
		{if $smarty.section.i.index lt $average_total}
                        <div class="star star_on"><a href=""></a></div>
		{else}
			<div class="star"><a href=""></a></div>			
		{/if}
	{/section}
	</div>

We are simply taking advantage of the section helper that smarty provides, allowing us to iterate a block of code for a defined number of times; in this case, 5. Inside the loop, we check the average total for the product. If it’s bigger than the current iteration index, it means the current star element must be active, if not, it must be grayed out. Save and refresh, you should get something like this:

Prestashop product rating in Products List - Almost Finished

Messy. Let’s clean it up a bit. Go to your theme folder and open up css/product_list.css. Locate #product_list li h3.

	#product_list li h3 {
		padding:0 0 10px 0;
		font-size:13px;
		color:#000
	}

Change that padding to 0. All of it. Then, add the following at the end of the file (or where you want actually, it doesn’t matter):

#product_list li .star_content  
{
	display: inline-block;
	margin-bottom: 10px;
}

Save and refresh. We are done! this is the end result. As you can see, the current product rating is now displaying correctly below each name:

Prestashop product rating in Products List - End result

You like the tuts and want to say "thank you"? Well, you can always feel free to donate:

  • Kon Rad

    Not working 1.5.6.2…
    In new hook i see something like duplicate of the product

    function hookProductInList($params)
    {
    require_once(dirname(__FILE__).’/ProductComment.php’);
    require_once(dirname(__FILE__).’/ProductCommentCriterion.php’);

    $id_product = $params[‘idproduct’];
    $product_average = 0;

    $grades = ProductComment::getAveragesByProduct($id_product, $this->context->language->id);
    $criterions = ProductCommentCriterion::getByProduct($id_product, $this->context->language->id);
    $grade_total = 0;

    if (count($grades) > 0)
    {
    foreach ($criterions as $criterion)
    {
    if (isset($grades[$criterion[‘id_product_comment_criterion’]]))
    {
    $grade_total += (float)($grades[$criterion[‘id_product_comment_criterion’]]);
    }
    }

    $product_average = $grade_total / count($criterions);
    }
    $this->context->smarty->assign(‘average_total’, (int)$product_average);
    return $this->display(__FILE__, ‘/product-list.tpl’);

    }

    {section name=”i” start=0 loop=5 step=1}
    {if $smarty.section.i.index lt $average_total}

    {else}

    {/if}
    {/section}

    o.O
    Help?

  • illiachenvar

    great tutorial – THANK YOU!

    and works with PS 1.5.6.1 :)

  • jonbzode

    Hello Nemo,
    Awesome code, just getting into prestashop coming from pure php/web stuff, and very nice it is too.
    I’ve added a little code to this that I thought I’d share (very minor addition!)

    changing your product-list.tpl from:

    {section name=”i” start=0 loop=5 step=1}
    {if $smarty.section.i.index lt $average_total}

    {else}

    {/if}
    {/section}

    To:

    {if $smarty.section.i.index lt $average_total == ‘0’}

    {else}

    {section name=”i” start=0 loop=5 step=1}
    {if $smarty.section.i.index lt $average_total}

    {else}

    {/if}
    {/section}

    {/if}

    Just a small addition to make no stars show up if the average is 0 or there has been no comments left yet.
    Keep Up The Good Work!
    Jonbzode

  • American Patriot

    While I did finally get it to work I have a request from the “I want everything” department.

    Is it possible to add, to the right of the stars, the average rating along with the number of reviews in parenthesis, much like the Best Buy website ratings? Something like:

    gold star;gold star;gold star;grey star;grey star 3.6 out of 5 (44 Reviews)

    Thanks Nemo for a great tutorial!

  • American Patriot

    Does this work in Prestashop 1.5.5? Regardless of what I try I can’t get it to work. When I place the code in the productcomments.php file, create the product-list.tpl file, and refresh my page all I get is a blank page in Firefox and Chrome just says “Server error”.

    I copied the code that Juda provided and added it just above the last } in my productcomments.php file. Am I doing something wrong?

    Thanks!

  • PascalVG

    Hi Nemo,

    Tiny detail, which caused some problem to someone on the forum:

    Quote:
    Step 3 – Creating the hooking function
    So, we have our hook. Let’s test it out. At the end of productcomments.php create a new function

    They took your suggestion a little too literally, and added it fully at the end of the file, OUTSIDE the class…

    So, we have to make sure it’s JUST ABOVE the very last “}”, to keep the function INSIDE the class, to make it part of it:

    add our code here
    }
    — END OF FILE–

    Hope this helps,
    pascal

  • SUNN78

    Sorry, It was my fault…

    I copied wrong the product-list.tpl

    Now works fine

    thanks

  • SUNN78

    Hi,

    When I add the last piece of code:

    $this->context->smarty->assign('average_total', (int)$product_average);
    return $this->display(__FILE__, '/product-list.tpl');

    My site broken it.

    Any idea?

  • http://www.freshsmoke.dk Davallen

    Hi Nemo,

    I tried to follow and was able to show the echo, but when I add the last function, I get this error
    Parse error: syntax error, unexpected T_IF, expecting T_FUNCTION in /var/www/freshsmoke.dk/public_html/modules/productcomments/productcomments.php on line 829

    Running 1.5.3.1

    • http://www.freshsmoke.dk Davallen

      I found the problem, forgot to put the other funtions inside of the first (inside of the last })

      If anyone wants to deactivate the hover on the stars and the pointer and just want them to be showing without being clickable, add pointer-events: none;
      cursor: default;

      to #product_list li .star_content
      #product_list li .star_content {
      display: inline-block;
      margin-bottom: 10px;
      pointer-events: none;
      cursor: default;
      }

  • Franklin

    Thanks for the tutorial..!

  • probytes

    I am trying to display product rating in product listing page in prestashop 1.5.4.1. Following this tutorial i didnt get any out put. Even i didnt get – echo ‘hey there';

  • Xezus

    Thanks for this amazing article Nemo!!

    But thanks to this modification is now the module “no nativ” and so I would like to export this modul with my theme as well but import/export modul didn’t notice that the modul is no longer nativ and don’t add this module to exported modules list.

    How can I add the module to this list?

  • RektorSkinner

    Hi there,
    first of all realy nice tutorial!

    But i’ve got the same question like Florio before.
    You left the href empty wich causes a simple page:


    <div class="star_content clearfix">
    {section name="i" start=0 loop=5 step=1}
    {if $smarty.section.i.index lt $average_total}
    <div class="star star_on"><a href=""></a></div>
    {else}
    <div class="star"><a href=""></a></div>
    {/if}
    {/section}
    </div>

    So where do i send the ratings to?

    • lim

      how to delete the new word ?

  • http://nowinworking,inlocalhost Florio

    What is the path to be inserted to href : ?

    Thanks!

  • Florio

    Hi Nemo,

    Is great the code script. I installed but my stars is grey the not change color.
    The link not change number of the path, the path is: index.php?id_manufacturer=1&controller=manufacturer&p=2 .
    I can move on another star cursor, the path is identical, the not change path.

    You can help me please?

    Thanks!

  • http://comp.xsurvive.org/en/5-laptops Momchil

    I don’t know how you manage to work on your sites but i do everything step by step and stars didn’t appear at all. Same with the pictures in shop card tutorial. Can anyone help ?

  • Cari

    I’m guessing this is for experts who know code, i’m not one and though i got through the first parts ok, the last parts caused problems.

    When adding:
    function hookProductInList($params)
    {
    require_once(dirname(__FILE__).’/ProductComment.php’);
    require_once(dirname(__FILE__).’/ProductCommentCriterion.php’);

    $id_product = $params[‘idproduct’];
    $product_average = 0;

    $grades = ProductComment::getAveragesByProduct($id_product, $this->context->language->id);
    $criterions = ProductCommentCriterion::getByProduct($id_product, $this->context->language->id);
    $grade_total = 0;
    }

    You say to add “if (count($grades) > 0) etc” after it. You didn’t mention that a “}” is required in between the two codes (without this my site breaks, i had to figure it out through trial and error and it took a long time).

    Then you say to add the following after the if(count…. etc…..
    $this->context->smarty->assign(‘average_total’, (int)$product_average);
    return $this->display(__FILE__, ‘/product-list.tpl’);

    but again, my site breaks. i tried adding a “}” between the two codes. I left “}” out. I added it at the end, i added one between the two codes and at the end, it’s just not working.

    Would you be able to clarify this?

    Thanks

    • Juda

      Working like a charm ! Thanks !
      I even add it on the homepage.

      The final code of the function is supposed to look like this :

      function hookProductInList($params)
      {
      require_once(dirname(__FILE__).’/ProductComment.php’);
      require_once(dirname(__FILE__).’/ProductCommentCriterion.php’);

      $id_product = $params[‘idproduct’];
      $product_average = 0;

      $grades = ProductComment::getAveragesByProduct($id_product, $this->context->language->id);
      $criterions = ProductCommentCriterion::getByProduct($id_product, $this->context->language->id);
      $grade_total = 0;

      if (count($grades) > 0)
      {
      foreach ($criterions as $criterion)
      {
      if (isset($grades[$criterion[‘id_product_comment_criterion’]]))
      {
      $grade_total += (float)($grades[$criterion[‘id_product_comment_criterion’]]);
      }
      }

      $product_average = $grade_total / count($criterions);
      }
      $this->context->smarty->assign(‘average_total’, (int)$product_average);
      return $this->display(__FILE__, ‘/product-list.tpl’);

      }

  • http://www.cgu.com.es/ currogüeb

    Great!
    I have it working 100% on site http://www.comercialeuroahorro.es perfectly.
    Now I will try to add some code for Google rich snnipets.
    Thank you very much.
    Best regards,
    Paco.

  • http://www.decorativ-art.ro alaincostea

    Hello,
    I’ve made the changes and it works.
    But, ( because always must be a but… :) ), on the main page, to the featured products and new products, the stars aren’t there. I don’t know if they should, but it will be more intersting to be, because there is the first look for the client…

    Anyway, great, and good explaind article.
    Thank you!

    • http://nemops.com Nemo

      No they’re not supposed to be there, since the hook is only added to the product list tpl file, and those ones use their own. You have to modify those ones and add the hook there too, the,, they *should* appear :D

  • pascalVG

    Hi Nemo.

    Thanks! Great article. Never played that much with hooks yet, and this shows clearly the power of it. Very elegant :-)

    Pascal

    N.B.
    A few small typo’s:
    Just below ‘Step 2″:
    “Open up product.list.tpl” –> change ‘.’ into ‘-‘, thus product-list.tpl

    Very last sentence:
    is now displaying correclt below each name –> correclt change to ‘correctly’

    One written explanation you give about giving stars is exactly the opposite of what you write in the code:
    Quote:
    if it [i.e. the average grade] equals, or it’s lower than the current iteration index, it means [i.e. comparison = ‘true’] ‘the current star element must be active, if not [comparison = ‘false’ -> ‘else statement’], it must be grayed out.

    This says if the average grade is lower than or equal the counter it must be an ACTIVE star (which is incorrect). in your code (result when true) you give it a grey star, (which is correct). When average is greater, in the explanation you give it a greyed out star (which is incorrect), In your code it gets an ACTIVE STAR (which is correct). So the code is opposite of your explanation. Hope I made myself clear.

    To summarise, the code is correct, but the written story tells exactly the opposite of the code.

    For ‘easier explanatory’ reasons, I would change the code into:
    {if $smarty.section.i.index lt $average_total}

    {else}

    {/if}

    and then write:
    If the the the current step is lower than the average grade, it means the current star must be active, otherwise it must be greyed out.

    Hope this helps,

    Thanks again for the great piece,
    Pascal

    • http://nemops.com Nemo

      Uh, you’re right, thanks! Correxcting right away :)

      Thanks so much for your attention, really

      • pascalVG

        My pleasure.

        Thanks for this great example of using hooks. I’m totally ‘hooked’ :-)

        Pascal

Store Top Sales

You like the tuts and want to say "thank you"? Well, you can always feel free to donate: