Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save milanu13/766cbd978864b2b31a900a3cf0bd176a to your computer and use it in GitHub Desktop.
Save milanu13/766cbd978864b2b31a900a3cf0bd176a to your computer and use it in GitHub Desktop.
Magento 2 - Custom menu module with thumbnail images.

Magento 2 - Custom menu module with thumbnail images.

Code example of custom Magento 2 menu for adding thumnail images.

app/code/Milandev/CustomMenu/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="Milandev_CustomMenu" setup_version="1.0.0">
		<sequence>
			<module name="Magento_Catalog"/>
		</sequence>
	</module>
</config>

app/code/Milandev/CustomMenu/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Milandev_CustomMenu',
    __DIR__
);

app/code/Milandev/CustomMenu/Setup/InstallData.php

<?php
namespace Milandev\CustomMenu\Setup;

use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;

class InstallData implements InstallDataInterface
{

    private $eavSetupFactory;

    /**
     * Constructor
     *
     * @param \Magento\Eav\Setup\EavSetupFactory $eavSetupFactory
     */
    public function __construct(EavSetupFactory $eavSetupFactory)
    {
        $this->eavSetupFactory = $eavSetupFactory;
    }

    /**
     * {@inheritdoc}
     */
    public function install(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ) {
        $eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);

        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Category::ENTITY,
            'cat_thumbnail',
            [
                'type' => 'varchar',
                'label' => 'Thumbnail',
                'input' => 'image',
                'sort_order' => 333,
                'source' => '',
                'global' => 1,
                'visible' => true,
                'required' => false,
                'user_defined' => false,
                'default' => null,
                'group' => 'General Information',
                'backend' => 'Magento\Catalog\Model\Category\Attribute\Backend\Image'
            ]
        );
    }
}

app/code/Milandev/CustomMenu/view/adminhtml/ui_component/category_form.xml

<?xml version="1.0" ?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
	<fieldset name="general">
		<field name="cat_thumbnail">
			<argument name="data" xsi:type="array">
				<item name="config" xsi:type="array">
					<item name="required" xsi:type="boolean">false</item>
					<item name="validation" xsi:type="array">
						<item name="required-entry" xsi:type="boolean">false</item>
					</item>
					<item name="sortOrder" xsi:type="number">333</item>
					<item name="dataType" xsi:type="string">string</item>
					<item name="formElement" xsi:type="string">fileUploader</item>
					<item name="label" translate="true" xsi:type="string">Thumbnail</item>
					<item name="uploaderConfig" xsi:type="array">
						<item name="url" path="catalog/category_image/upload" xsi:type="url"/>
					</item>
					<item name="elementTmpl" xsi:type="string">ui/form/element/uploader/uploader</item>
					<item name="previewTmpl" xsi:type="string">Magento_Catalog/image-preview</item>
				</item>
			</argument>
		</field>
	</fieldset>
</form>

app/code/Milandev/CustomMenu/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="Milandev\CustomMenu\Block\Html\Topmenu" />
</config>

app/code/Milandev/CustomMenu/Block/Html/Topmenu.php

<?php
namespace Milandev\CustomMenu\Block\Html;
 
use Magento\Framework\Data\Tree\Node;
use Magento\Framework\DataObject;
use Magento\Framework\View\Element\Template;
use Magento\Framework\Data\Tree\NodeFactory;
use Magento\Framework\Data\TreeFactory;
 
class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
    protected $_categoryFactory;
    protected $_storeManager;
    
    public function __construct(
        Template\Context $context,
        NodeFactory $nodeFactory,
        TreeFactory $treeFactory,
        \Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $collecionFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        array $data = []
        ) {
            parent::__construct($context, $nodeFactory, $treeFactory, $data);
            $this->_categoryFactory = $collecionFactory;
            $this->_storeManager = $storeManager;
        
    }

    protected function _getHtml(
        \Magento\Framework\Data\Tree\Node $menuTree,
        $childrenWrapClass,
        $limit,
        array $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-';

        /** @var \Magento\Framework\Data\Tree\Node $child */
        foreach ($children as $child) {
            if ($childLevel === 0 && $child->getData('is_parent_active') === false) {
                continue;
            }
            $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 . '" ';
                $currentClass = $child->getClass();

                if (empty($currentClass)) {
                    $child->setClass($outermostClass);
                } else {
                    $child->setClass($currentClass . ' ' . $outermostClass);
                }
            }

            if (is_array($colBrakes) && 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()
                ) . $this->getCustomThumbnail($child) . '</span></a>' . $this->_addSubMenu(
                $child,
                $childLevel,
                $childrenWrapClass,
                $limit
            ) . '</li>';
            $itemPosition++;
            $counter++;
        }

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

        return $html;
    }

    public function getCustomThumbnail($childObj)
    {
        if (!($childObj->getIsCategory() && $childObj->getLevel() == 1)) {
            return false;
        }

        $store = $this->_storeManager->getStore();
        $mediaBaseUrl = $store->getBaseUrl(
            \Magento\Framework\UrlInterface::URL_TYPE_MEDIA
        );

        $catNodeArr = explode('-', $childObj->getId());
        $catId = end($catNodeArr);
        
        $collection = $this->_categoryFactory
                ->create()
                ->addAttributeToSelect('cat_thumbnail')
                ->addAttributeToFilter('entity_id',['eq'=>$catId])
                ->setPageSize(1);
        
        if ($collection->getSize() && $collection->getFirstItem()->getCatThumbnail()) {
            $catThumbnailUrl = $mediaBaseUrl
                        . ltrim(\Magento\Catalog\Model\Category\FileInfo::ENTITY_MEDIA_PATH, '/')
                        . '/'
                        . $collection->getFirstItem()->getCatThumbnail();

            return '<span class="cat-thumbnail"><img src="'.$catThumbnailUrl.'"></span>';
        }
    }

}

app/code/Milandev/CustomMenu/view/frontend/layout/default_head_blocks.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <head>
        <css src="Milandev_CustomMenu::css/cmenu.css" />
    </head>
</page>

app/code/Milandev/CustomMenu/view/frontend/web/css/cmenu.css

/* add custom menu css */
@dennismetz
Copy link

Thanks for the code!
This contained the crucial hint how I can read attributes in the frontend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment