4

How to display the category image in the topmenu?

I need to edit/overwrite the block file, but I can't get the code to actually extract the image.

Ashish Jagnani
  • 6,266
  • 7
  • 33
  • 70
Memes
  • 708
  • 2
  • 13
  • 21

1 Answers1

13

Look at

vendor/magento/module-catalog/etc/di.xml

<type name="Magento\Theme\Block\Html\Topmenu">
        <plugin name="catalogTopmenu" type="Magento\Catalog\Plugin\Block\Topmenu" />
</type>

As we can see, Magento uses Plugin - Magento\Catalog\Plugin\Block\Topmenu to override some methods of Magento\Theme\Block\Html\Topmenu class. We should see more vendor/magento/module-catalog/Plugin/Block/Topmenu.php.

We can disable the default plugin in your module and re-build your own Plugin which follow the logic of Magento\Catalog\Plugin\Block\Topmenu class.

Vendor\Module\etc\di.xml

 <type name="Magento\Theme\Block\Html\Topmenu">
        <plugin name="catalogTopmenu" disabled="true" />
        <plugin name="catalogTopmenuImage" type="Vendor\Module\Plugin\Block\Topmenu" />
 </type>

From my understanding, we should to rebuild some methods:

vendor/magento/module-catalog/Plugin/Block/Topmenu.php

...

/**
 * Convert category to array
 *
 * @param \Magento\Catalog\Model\Category $category
 * @param \Magento\Catalog\Model\Category $currentCategory
 * @return array
 */
private function getCategoryAsArray($category, $currentCategory)
{
    return [
        'name' => $category->getName(),
        'id' => 'category-node-' . $category->getId(),
        'url' => $this->catalogCategory->getCategoryUrl($category),
        'has_active' => in_array((string)$category->getId(), explode('/', $currentCategory->getPath()), true),
        'is_active' => $category->getId() == $currentCategory->getId(),
        'image_url' => $category->getImageUrl(), // Get image URL
    ];
}

/**
 * Get Category Tree
 *
 * @param int $storeId
 * @param int $rootId
 * @return \Magento\Catalog\Model\ResourceModel\Category\Collection
 * @throws \Magento\Framework\Exception\LocalizedException
 */
protected function getCategoryTree($storeId, $rootId)
{
    /** @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */
    $collection = $this->collectionFactory->create();
    $collection->setStoreId($storeId);
    $collection->addAttributeToSelect('name');
    $collection->addAttributeToSelect('image'); // Select image
    $collection->addFieldToFilter('path', ['like' => '1/' . $rootId . '/%']); //load only from store root
    $collection->addAttributeToFilter('include_in_menu', 1);
    $collection->addIsActiveFilter();
    $collection->addUrlRewriteToResult();
    $collection->addOrder('level', Collection::SORT_ORDER_ASC);
    $collection->addOrder('position', Collection::SORT_ORDER_ASC);
    $collection->addOrder('parent_id', Collection::SORT_ORDER_ASC);
    $collection->addOrder('entity_id', Collection::SORT_ORDER_ASC);

    return $collection;
}

...

You can then use $child->getDataByKey('image_url') in Topmenu::_getHtml() (via an extending module, as explained here). For example, the below will replace the category name with the image (if there is one):

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>';

        // Use category image instead of name if available
        $html .= $child->getDataByKey('image_url') ? '<img src="' . $child->getDataByKey('image_url') . '">' : $this->escapeHtml($child->getName());

        $html .= '</span></a>' . $this->_addSubMenu(
            $child,
            $childLevel,
            $childrenWrapClass,
            $limit
        ) . '</li>';
        $itemPosition++;
        $counter++;
    }

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

    return $html;
}
Khoa TruongDinh
  • 32,054
  • 11
  • 88
  • 155
  • I saw that already, but what is the code to get the image? – Memes Aug 16 '16 at 13:12
  • We can disable the default plugin in your module and re-build your own Plugin which follows the logic of Magento\Catalog\Plugin\Block\Topmenu class. – Khoa TruongDinh Aug 16 '16 at 13:31
  • I do not have any folder vendor/magento/module-catalog/Plugin/Block. In vendor/magento/module-catalog/Plugin , I just got Model – Memes Aug 17 '16 at 01:59
  • I used Magento version 2.1: vendor/magento/module-catalog/Plugin/Block/Topmenu.php. Please check again. – Khoa TruongDinh Aug 17 '16 at 02:25
  • I was still in 2.0.7, doing the upgrade now – Memes Aug 17 '16 at 02:36
  • Hi @Khoa TruongDinh, how to get category images along with the links, ie) i need to category image for 1st category, for 2nd category, need to get default magento links, 3rd category need to get both images and links, could you please suggest on this? – Sushivam Feb 20 '17 at 06:42
  • 1
    A solution with the same principle but cleaner can be found here: http://catgento.com/2017/03/20/magento-2-display-category-image-in-navigation-menu . Actually, it's not necessary to rewrite all the code, because the _getHtml function calls itself again and again. – PauGNU Apr 22 '17 at 04:11
  • Hi @KhoaTruongDinh, i have tried this but the image also appears on category page. i only want image with category name as link in navigation menu for mobile view only. Can u pls help me in this. – Ankita Biswas Jul 11 '19 at 07:20
  • getCategoryAsArray method is private how can we modify it? – Manish Goswami Feb 10 '20 at 11:28