Add Google Rich Snippets Breadcrumbs to Prestashop

In this tutorial we will see how to add Google Rich Snippets Breadcrumbs to a Prestashop website, by modifying a small portion of the template and using a lightweight override.

  • Version used: Prestashop 1.5.6.2

Two types of markup

Google supports the following types of data for the breadcrumbs rich snippets:

  • Microdata
  • RDFa

Microdata is the one we used in the previous tutorial on how to add Google Rich Snippets to your Prestashop store, and the basic markup consists in adding itemprops and scopes to single html entities

On the other hand, RDFa adds a set of attributes and extensions to html using xml properties. You can read more about it here: RDFa Wiki.

I will use the latter in this demonstration, but either will do, so feel free to use the one you prefer in the end. You can visit the official Google Documentation on Breadcrumbs Rich Snippets if you want to get more info about the implementation.

Adding Google Rich Snippets Breadcrumbs to Prestashop – The tpl

First things first, we must hardcode a small portion of code inside breadcrumb.tpl. Open up the file located in your theme’s folder. It will look approximately like this (default theme):

<!-- Breadcrumb -->
{if isset($smarty.capture.path)}{assign var='path' value=$smarty.capture.path}{/if}
<div class="breadcrumb">
	<a href="{$base_dir}" title="{l s='Return to Home'}"><img src="{$img_dir}icon/home.gif" height="26" width="26" alt="{l s='Home'}" /></a>
	{if isset($path) AND $path}
		<span class="navigation-pipe" {if isset($category) && isset($category->id_category) && $category->id_category == 1}style="display:none;"{/if}>{$navigationPipe|escape:html:'UTF-8'}</span>
		{if !$path|strpos:'span'}
			<span class="navigation_page">{$path}</span>
		{else}
			{$path}
		{/if}
	{/if}
</div>
<!-- /Breadcrumb -->

Edit as follows:

<!-- Breadcrumb -->
{if isset($smarty.capture.path)}{assign var='path' value=$smarty.capture.path}{/if}
<div class="breadcrumb" xmlns:v="http://rdf.data-vocabulary.org/#">
	<span typeof="v:Breadcrumb"><a href="{$base_dir}" rel="v:url" property="v:title" title="{l s='Return to Home'}"><img src="{$img_dir}icon/home.gif" height="26" width="26" alt="{l s='Home'}" /></a></span>
	{if isset($path) AND $path}
		<span class="navigation-pipe" {if isset($category) && isset($category->id_category) && $category->id_category == 1}style="display:none;"{/if}>{$navigationPipe|escape:html:'UTF-8'}</span>
		{if !$path|strpos:'span'}
			<span typeof="v:Breadcrumb">
				<span property="v:title" class="navigation_page">{$path}</span>
			</span>
		{else}
			{$path}
		{/if}
	{/if}
</div>
<!-- /Breadcrumb -->

Explanation: First, we told Google that the .breadcrumb div is our breadcrumb container. Then, we added a span around the homepage link, with typeof=”v:Breadcrumb” so that it know it’s dealing with part of the breadcrumb. Lastly, we added a rel and property attribute to the link pointing to the homepage. Then, we added the breadcrumb object and title property to the last item

At this point, we must use an override as Prestashop builds breadcrumbs in the Tools::getPath() method!

Adding Google Rich Snippets Breadcrumbs to Prestashop – The override

Create a new override in override/classes and call it Tools.php. Add some regular override markup inside:


class Tools extends ToolsCore
{

}

We need to override the getPath method, so open the original Tools.php class file that you find in your classes/ directory, copy the whole getPath() method, and paste it inside the new override as follows:


class Tools extends ToolsCore
{
	public static function getPath($id_category, $path = '', $link_on_the_item = false, $category_type = 'products', Context $context = null)
	{
		if (!$context)
			$context = Context::getContext();

		$id_category = (int)$id_category;
		if ($id_category == 1)
			return '<span class="navigation_end">'.$path.'</span>';

		$pipe = Configuration::get('PS_NAVIGATION_PIPE');
		if (empty($pipe))
			$pipe = '>';

		$full_path = '';
		if ($category_type === 'products')
		{
			$interval = Category::getInterval($id_category);
			$id_root_category = $context->shop->getCategory();
			$interval_root = Category::getInterval($id_root_category);
			if ($interval)
			{
				$sql = 'SELECT c.id_category, cl.name, cl.link_rewrite
						FROM '._DB_PREFIX_.'category c
						LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = c.id_category'.Shop::addSqlRestrictionOnLang('cl').')
						WHERE c.nleft <= '.$interval['nleft'].'
							AND c.nright >= '.$interval['nright'].'
							AND c.nleft >= '.$interval_root['nleft'].'
							AND c.nright <= '.$interval_root['nright'].'
							AND cl.id_lang = '.(int)$context->language->id.'
							AND c.active = 1
							AND c.level_depth > '.(int)$interval_root['level_depth'].'
						ORDER BY c.level_depth ASC';
				$categories = Db::getInstance()->executeS($sql);

				$n = 1;
				$n_categories = count($categories);
				foreach ($categories as $category)
				{
					$full_path .=
					(($n < $n_categories || $link_on_the_item) ? '<a href="'.Tools::safeOutput($context->link->getCategoryLink((int)$category['id_category'], $category['link_rewrite'])).'" title="'.htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').'">' : '').
					htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').
					(($n < $n_categories || $link_on_the_item) ? '</a>' : '').
					(($n++ != $n_categories || !empty($path)) ? '<span class="navigation-pipe">'.$pipe.'</span>' : '');
				}

				return $full_path.$path;
			}
		}
		else if ($category_type === 'CMS')
		{
			$category = new CMSCategory($id_category, $context->language->id);
			if (!Validate::isLoadedObject($category))
				die(Tools::displayError());
			$category_link = $context->link->getCMSCategoryLink($category);

			if ($path != $category->name)
				$full_path .= '<a href="'.Tools::safeOutput($category_link).'">'.htmlentities($category->name, ENT_NOQUOTES, 'UTF-8').'</a><span class="navigation-pipe">'.$pipe.'</span>'.$path;
			else
				$full_path = ($link_on_the_item ? '<a href="'.Tools::safeOutput($category_link).'">' : '').htmlentities($path, ENT_NOQUOTES, 'UTF-8').($link_on_the_item ? '</a>' : '');

			return Tools::getPath($category->id_parent, $full_path, $link_on_the_item, $category_type);
		}
	}
}

We can now modify the code snippets that generate links. First off, locate:

		if (!$context)
			$context = Context::getContext();

Right after it, add

	$path = '<span typeof="v:Breadcrumb"><span property="v:title">'.$path.'</span></span>';

This way we label the current page name as title in the breadcrumbs. Then:

					$full_path .=
					(($n < $n_categories || $link_on_the_item) ? '<a href="'.Tools::safeOutput($context->link->getCategoryLink((int)$category['id_category'], $category['link_rewrite'])).'" title="'.htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').'">' : '').
					htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').
					(($n < $n_categories || $link_on_the_item) ? '</a>' : '').

Edit as follows

					$full_path .=
					(($n < $n_categories || $link_on_the_item) ? '<span typeof="v:Breadcrumb"> <a rel="v:url" property="v:title" href="'.Tools::safeOutput($context->link->getCategoryLink((int)$category['id_category'], $category['link_rewrite'])).'" title="'.htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').'">' : '').
					htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').
					(($n < $n_categories || $link_on_the_item) ? '</a> </span>' : '').

Explanation: as we did previously, we added a span around the link, and assigned a rel and property attributes to the inner link. Remember to close the span at the end!.

Lastly, let’s account for cms pages as well, find:

			if ($path != $category->name)
				$full_path .= '<a href="'.Tools::safeOutput($category_link).'">'.htmlentities($category->name, ENT_NOQUOTES, 'UTF-8').'</a><span class="navigation-pipe">'.$pipe.'</span>'.$path;
			else
				$full_path = ($link_on_the_item ? '<a href="'.Tools::safeOutput($category_link).'">' : '').htmlentities($path, ENT_NOQUOTES, 'UTF-8').($link_on_the_item ? '</a>' : '');

And change it to:

			if ($path != $category->name)
				$full_path .= '<span typeof="v:Breadcrumb"> <a rel="v:url" property="v:title" href="'.Tools::safeOutput($category_link).'">'.htmlentities($category->name, ENT_NOQUOTES, 'UTF-8').'</a> </span> <span class="navigation-pipe"> '.$pipe.' </span> '.$path;
			else
				$full_path = ($link_on_the_item ? '<span typeof="v:Breadcrumb"> <a rel="v:url" property="v:title" href="'.Tools::safeOutput($category_link).'">' : '').htmlentities($path, ENT_NOQUOTES, 'UTF-8').($link_on_the_item ? '</a> <span>' : '');

We are done! Save, clear smarty cache/force compile so that changes take effect, then go to cache/ and erase class_index.php so that the override can take place!

The final override:


class Tools extends ToolsCore
{


	public static function getPath($id_category, $path = '', $link_on_the_item = false, $category_type = 'products', Context $context = null)
	{
		if (!$context)
			$context = Context::getContext();

		$path = '<span typeof="v:Breadcrumb"><span property="v:title">'.$path.'</span></span>';

		$id_category = (int)$id_category;
		if ($id_category == 1)
			return '<span class="navigation_end">'.$path.'</span>';

		$pipe = Configuration::get('PS_NAVIGATION_PIPE');
		if (empty($pipe))
			$pipe = '>';

		$full_path = '';
		if ($category_type === 'products')
		{
			$interval = Category::getInterval($id_category);
			$id_root_category = $context->shop->getCategory();
			$interval_root = Category::getInterval($id_root_category);
			if ($interval)
			{
				$sql = 'SELECT c.id_category, cl.name, cl.link_rewrite
						FROM '._DB_PREFIX_.'category c
						LEFT JOIN '._DB_PREFIX_.'category_lang cl ON (cl.id_category = c.id_category'.Shop::addSqlRestrictionOnLang('cl').')
						WHERE c.nleft <= '.$interval['nleft'].'
							AND c.nright >= '.$interval['nright'].'
							AND c.nleft >= '.$interval_root['nleft'].'
							AND c.nright <= '.$interval_root['nright'].'
							AND cl.id_lang = '.(int)$context->language->id.'
							AND c.active = 1
							AND c.level_depth > '.(int)$interval_root['level_depth'].'
						ORDER BY c.level_depth ASC';
				$categories = Db::getInstance()->executeS($sql);

				$n = 1;
				$n_categories = count($categories);
				foreach ($categories as $category)
				{
					$full_path .=
					(($n < $n_categories || $link_on_the_item) ? '<span typeof="v:Breadcrumb"> <a rel="v:url" property="v:title" href="'.Tools::safeOutput($context->link->getCategoryLink((int)$category['id_category'], $category['link_rewrite'])).'" title="'.htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').'">' : '').
					htmlentities($category['name'], ENT_NOQUOTES, 'UTF-8').
					(($n < $n_categories || $link_on_the_item) ? '</a> </span>' : '').
					(($n++ != $n_categories || !empty($path)) ? ' <span class="navigation-pipe"> '.$pipe.' </span> ' : '');
				}

				return $full_path.$path;
			}
		}
		else if ($category_type === 'CMS')
		{
			$category = new CMSCategory($id_category, $context->language->id);
			if (!Validate::isLoadedObject($category))
				die(Tools::displayError());
			$category_link = $context->link->getCMSCategoryLink($category);

			if ($path != $category->name)
				$full_path .= '<span typeof="v:Breadcrumb"> <a rel="v:url" property="v:title" href="'.Tools::safeOutput($category_link).'">'.htmlentities($category->name, ENT_NOQUOTES, 'UTF-8').'</a> </span> <span class="navigation-pipe"> '.$pipe.' </span> '.$path;
			else
				$full_path = ($link_on_the_item ? '<span typeof="v:Breadcrumb"> <a rel="v:url" property="v:title"  href="'.Tools::safeOutput($category_link).'">' : '').htmlentities($path, ENT_NOQUOTES, 'UTF-8').($link_on_the_item ? '</a> <span>' : '');

			return Tools::getPath($category->id_parent, $full_path, $link_on_the_item, $category_type);
		}
	}

}

Time to test your page now! Here is one from my Prestashop modules store: Prestashop SEO Booster example

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

  • NemoPS

    If you are using a recent 1.6 template, or a non standard one for 1.5, make sure original snippets are not conflicting with the ones you add

  • Will

    Hi Nemo, I tried to implement your tutorial about breadcrumbs. However, when I checked any of my product pages on Google Structured Data Testing Tool, it doesn’t work as I have no breadcrumbs appeared. What am I doing wrong? For instance, one product webpage is http://www.nizoralshop.com/en/shampoos/30-shampoo-scalp-massage-brush.html
    Can you please help me? Thank you!

  • rob
  • Prestashop User

    I made the changes. And it works when “testing” it. Any idea on how long until Google picks this up so it automatically shows the breadcrumbs in google search results?

  • Edgar

    Hello,

    for the right $path please change the:

    $path = ”.$path.”;

    to

    if(!empty($path))
    $path = ”.$path.”;

Store Top Sales

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