[Magento 2] Display category image in navigation menu

Recently I had to do something I thought it was simple to do: add the category images to the navigation menu. However, Magento doesn’t use a template to draw the whole menu but a function that will retrieve all categories and generate the ul/li tree.

In Magento 2.1.X you can do this rewriting the _getHtml() function from the class Magento\Theme\Block\Html\Topmenu.

1. Create your module

Create a foder in app/code/Vendor/NavigationMenu, where ‘vendor’ is your namespace for your modules.
app/code/Vendor/NavigationMenu/etc/module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Vendor_NavigationMenu" setup_version="0.0.1"/></config>

Now the module registration:

<?php
/**
 * Registration file
 *
 * @category  Vendor
 * @package   Vendor\NavigationMenu
 * @author    Your Name <your@name.com>
 * @copyright 2017 Vendor
 * @license   http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Vendor_NavigationMenu',
    __DIR__
);

 

2. Rewrite the class Magento\Theme\Block\Html\TopMenu

Create the di.xml to declare the rewriting of the class where the _getHtml() method is called:
app/code/Vendor/NavigationMenu/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Theme\Block\Html\Topmenu" type="Vendor\NavigationMenu\Rewrite\Block\Html\Topmenu" />
</config>

Now you can create the class that will rewrite the method. Now the following points:

  • We add the image to the final part of the code, adding a custom css class to correctly implement this in the design
  • We have to use the CategoryFactory to load the category image (remember to never use the ObjectManager!). We could have done this rewriting a different class – vendor/magento/module-catalog/Plugin/Block/Topmenu.php, but I preferred to keep everything in one file.
  • We had to add some warning supressions, because the _getHtml() Magento native function is not compliant to some coding standards

app/code/Vendor/NavigationMenu/

<?php
/**
 * Vendor Project
 * Module Vendor/NavigationMenu
 *
 * @category  Vendor
 * @package   Vendor\NavigationMenu
 * @author    Your Name <your@name.com>
 * @copyright 2017 Vendor
 * @license   http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 */
namespace Vendor\NavigationMenu\Rewrite\Block\Html;

use Magento\Framework\View\Element\Template;
use Magento\Framework\Data\TreeFactory;
use Magento\Framework\Data\Tree\NodeFactory;
use Magento\Catalog\Model\CategoryFactory;

/**
 * Plugin NavigationMenu
 *
 * @author Your Name <your@name.com>
 * @copyright 2017 Vendor
 */
class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
    /**
     * CategoryFactory
     *
     * @var CategoryFactory
     */
    protected $categoryFactory;

    /**
     * @codingStandardsIgnoreStart
     * @param Template\Context $context
     * @param NodeFactory $nodeFactory
     * @param TreeFactory $treeFactory
     * @param CategoryFactory $categoryFactory
     * @param array $data
     */
    public function __construct(
        Template\Context $context,
        NodeFactory $nodeFactory,
        TreeFactory $treeFactory,
        CategoryFactory $categoryFactory,
        array $data = []
    ) {
        $this->categoryFactory = $categoryFactory;

        parent::__construct($context, $nodeFactory, $treeFactory, $data);
        /* @codingStandardsIgnoreEnd */
    }

    /**
     * Retrieves the category image for the corresponding child
     *
     * @param string $categoryId Category composed ID
     *
     * @return string
     */
    public function getCategoryImage($categoryId)
    {
        $categoryIdElements = explode('-', $categoryId);

        $category = $this->categoryFactory->create();
        $category->load(end($categoryIdElements));

        $categoryName = $category->getImageUrl();

        return $categoryName;
    }

    /**
     * Recursively generates top menu html from data that is specified in $menuTree
     * @codingStandardsIgnoreStart
     *
     * @param \Magento\Framework\Data\Tree\Node $menuTree
     * @param string $childrenWrapClass
     * @param int $limit
     * @param array $colBrakes
     * @return string
     *
     * @SuppressWarnings(PHPMD)
     */
    protected function _getHtml(
        \Magento\Framework\Data\Tree\Node $menuTree,
        $childrenWrapClass,
        $limit,
        $colBrakes = []
    ) {
        $html = '';

        $children = $menuTree->getChildren();
        $parentLevel = $menuTree->getLevel();
        $childLevel = $parentLevel === null ? 0 : $parentLevel + 1;

        $counter = 1;
        $itemPosition = 1;
        $childrenCount = $children->count();

        $parentPositionClass = $menuTree->getPositionClass();
        $itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-';

        foreach ($children as $child) {
            $child->setLevel($childLevel);
            $child->setIsFirst($counter == 1);
            $child->setIsLast($counter == $childrenCount);
            $child->setPositionClass($itemPositionClassPrefix . $counter);

            $outermostClassCode = '';
            $outermostClass = $menuTree->getOutermostClass();

            if ($childLevel == 0 && $outermostClass) {
                $outermostClassCode = ' class="' . $outermostClass . '" ';
                $child->setClass($outermostClass);
            }

            if (count($colBrakes) && $colBrakes[$counter]['colbrake']) {
                $html .= '</ul>
</li>
<li class="column">
<ul>';
            }

            $html .= '
<li ' . $this->_getRenderedMenuItemAttributes($child) . '>';
            $html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>' . $this->escapeHtml(
                    $child->getName()
                ) . '</span></a>' . $this->_addSubMenu(
                    $child,
                    $childLevel,
                    $childrenWrapClass,
                    $limit
                ) . '</li>

';

            $itemPosition++;
            $counter++;
        }

        if (count($colBrakes) && $limit) {
            $html = '
<li class="column">
<ul>' . $html . '</ul>
</li>

';
        }

        if($childLevel == 1) {
            $html .= '
<li class="category_image" style=""><img src="'.$this->getCategoryImage($menuTree->getId()).'"/></li>

';
        }

        return $html;
        /* @codingStandardsIgnoreEnd */
    }
}

Leave a Reply

Your email address will not be published.