In this post, we will see how we can create a category menu.

First, we need to have our module and theme ready. Please follow the links below if you haven’t created them yet.

Creating module

Creating block

Creating theme

Now, we will create a new block named Menu.php in the following location:

app/code/Mymodule/Test/Block/Menu.php

Add following code to this file:

<?php
namespace Mymodule\Test\Block;

use Magento\Framework\View\Element\Template;
use Magento\Catalog\Helper\Category;
use Magento\Framework\View\Element\Template\Context;
use Magento\Catalog\Model\Indexer\Category\Flat\State;

class Menu
    extends Template
{
    protected $_categoryHelper;
    protected $categoryFlatConfig;

    public function __construct(
        Category $categoryHelper,
        State $categoryFlatState,
        Context $context
    ){

        $this->_categoryHelper = $categoryHelper;
        $this->categoryFlatConfig = $categoryFlatState;

        parent::__construct($context);
    }

    public function getRootCategories(){

        $categories = $this->_categoryHelper->getStoreCategories(true, false, true);

        return $categories;
    }

    public function getSubCategories($category)
    {
        if ($this->categoryFlatConfig->isFlatEnabled() && $category->getUseFlatResource()) {
            $subCategories = (array)$category->getChildrenNodes();
        } else {
            $subCategories = $category->getChildren();
        }
        return $subCategories;
    }

    public function getCategoryUrl($category){
        return $this->_categoryHelper->getCategoryUrl($category);
    }
}

Now, add following reference to your default.xml in your theme:

<referenceContainer name="header.container">
    <container name="common-header" label="Header common to all pages" as="common-header" htmlTag="div" htmlClass="main-header">
        <block class="Mymodule\Test\Block\Header" template="header.phtml">
            <block name="header.menu" class="Mymodule\Test\Block\Menu" template="menu.phtml"/>
        </block>
    </container>
</referenceContainer>

Here, we added menu.phtml as the child of header.phtml. To render(show) our menu, we can write following line anywhere inside our header.phtml:

<?php
$categoriesLevel0 = $this->getRootCategories();

if($categoriesLevel0) {

    echo "<ul>";

    foreach ($categoriesLevel0 as $categoryLevel0) {
?>
        <li><a href="<?php echo $this->getCategoryUrl($categoryLevel0); ?>"
               class="menu-heading"><?php echo $categoryLevel0->getName(); ?></a>

        <?php
        $categoriesLevel1 = $this->getSubCategories($categoryLevel0);

        if ($categoriesLevel1) {

            echo "<ul>";

            foreach ($categoriesLevel1 as $subCategoryLevel1) {

                echo "<li><a href='" . $this->getCategoryUrl($subCategoryLevel1) . "'>" . $subCategoryLevel1->getName() . "</a>";

                $categoriesLevel2 = $this->getSubCategories($subCategoryLevel1);

                if ($categoriesLevel2) {

                    echo "<ul>";

                    foreach ($categoriesLevel2 as $subCategoryLevel2) {
                        echo "<li><a href='" . $this->getCategoryUrl($subCategoryLevel2) . "'>" . $subCategoryLevel2->getName() . "</a></li>";
                    }

                    echo "</ul>";
                }

                echo "</li>";
            }

            echo "</ul>";
        }

        echo "</li>";
    }

    echo "</ul>";
}