Let’s say I have to show category images to navigation menu.
Magento doesn’t take a use of any kind of template to draw the whole menu, but a function which will retrieve all the categories & create li/ul tree.
In Magento 2.1.X you can easily perform this task by rewriting the _getHtml() function from the class located at MagentoThemeBlockHtmlTopmenu. It’s kind of function which will call to itself again-n-again. We will make an observer which will add our programmed code in the navigation menu.
1. Create your module
Let’s start by creating one folder in app/code/Vendor/ModuleName.
Here, ‘vendor’ is the namespace for your modules.
app/code/Vendor/ModuleName/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_ModuleName" setup_version="0.0.1"/></config>
Now the module registration:
<?php
/**
* Registration file
*
* @category Vendor
* @package VendorModuleName
* @author Your Name <your@name.com>
* @copyright 2017 Vendor
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
MagentoFrameworkComponentComponentRegistrar::register(
MagentoFrameworkComponentComponentRegistrar::MODULE,
'Vendor_ModuleName',
__DIR__
);
2. Rewrite this class MagentoThemeBlockHtmlTopMenu
Create a file called di.xml to declare the rewriting of the class where the _getHtml() method is going to get 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="MagentoThemeBlockHtmlTopmenu" type="VendorNavigationMenuRewriteBlockHtmlTopmenu" />
</config>
After this you are allowed to create the class which will allow to rewrite the method. Now the following points:
app/code/Vendor/NavigationMenu/Rewrite/Block/Html/Topmenu.php
<?php
/**
* Vendor Project
* Module Vendor/ModuleName
*
* @category Vendor
* @package VendorModuleName
* @author Your Name <your.name@email.com>
* @copyright 2017 Vendor
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
namespace VendorModuleNameRewriteBlockHtml;
use MagentoFrameworkDataTreeNode;
use MagentoFrameworkDataObject;
use MagentoFrameworkViewElementTemplate;
/**
* Plugin ModuleName
*
* @author Your Name <your.name@email.com>
* @copyright 2017 Vendor
*/
class Topmenu extends MagentoThemeBlockHtmlTopmenu
{
/**
* Recursively generates top menu html from data that is specified in $menuTree
*
* @param Node $menuTree menu tree
* @param string $childrenWrapClass children wrap class
* @param int $limit limit
* @param array $colBrakes column brakes
* @return string
*
* @SuppressWarnings(PHPMD)
*/
protected function _getHtml(
Node $menuTree,
$childrenWrapClass,
$limit,
$colBrakes = []
) {
$html = parent::_getHtml($menuTree, $childrenWrapClass, $limit, $colBrakes = []);
$transportObject = new DataObject(['html' => $html, 'menu_tree' => $menuTree]);
$this->_eventManager->dispatch(
'vendor_topmenu_node_gethtml_after',
['menu' => $this->_menu, 'transport' => $transportObject]
);
$html = $transportObject->getHtml();
return $html;
}
}
3. Make observer which will triggered by the event
To perform it we have to create the events.xml configuration file and assign the class which will handle the event:
app/code/Vendor/NavigationMenu/etc/frontend/events.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="vendor_topmenu_node_gethtml_after">
<observer name="vendor_modulename_topmenu" instance="VendorModuleNameObserverAddContentToCategoryTopmenu" />
</event>
</config>
app/code/Vendor/NavigationMenu/Observer/AddContentToCategoryTopmenu.php
<?php
/**
* Topmenu catalog observer to add custom additional elements
*
* @category Vendor
* @package VendorModuleName
* @author Your Name <your.name@email.com>
* @copyright 2017 Vendor
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
namespace VendorModuleNameObserver;
use MagentoFrameworkEventObserver;
use MagentoFrameworkEventObserverInterface;
use MagentoCatalogApiCategoryRepositoryInterface;
/**
* Class AddFirstCategoryImageToTopmenu
* @package VendorModuleName
*/
class AddContentToCategoryTopmenu implements ObserverInterface
{
/**
* @var CategoryRepositoryInterface $categoryRepository
*/
protected $categoryRepository;
/**
* AddFirstCategoryImageToTopmenu constructor.
*
* @param CategoryRepositoryInterface $categoryRepository repository
*/
public function __construct(
CategoryRepositoryInterface $categoryRepository
) {
$this->categoryRepository = $categoryRepository;
}
/**
* @param Observer $observer Observer object
*/
public function execute(Observer $observer)
{
$transport = $observer->getTransport();
$html = $transport->getHtml();
$menuTree = $transport->getMenuTree();
$parentLevel = $menuTree->getLevel();
$childLevel = $parentLevel === null ? 0 : $parentLevel + 1;
$menuId = $menuTree->getId();
if ($childLevel == 1 && $this->isCategory($menuId)) {
$html .= '<li class="category_image" style=""><img src="'.$this->getCategoryImage($menuId).'"/></li>';
}
$transport->setHtml($html);
}
/**
* Retrieves the category image for the corresponding child
*
* @param string $categoryId Category composed ID
*
* @return string
*/
protected function getCategoryImage($categoryId)
{
$categoryIdElements = explode('-', $categoryId);
$category = $this->categoryRepository->get(end($categoryIdElements));
$categoryName = $category->getImageUrl();
return $categoryName;
}
/**
* Check if current menu element corresponds to a category
*
* @param string $menuId Menu element composed ID
*
* @return string
*/
protected function isCategory($menuId)
{
$menuId = explode('-', $menuId);
return 'category' == array_shift($menuId);
}
}

