Magento – Magento2: List the all categories and sub categories in home page left sidebar

category-listinghome-pagelayered-navigationmagento2sidebar

I am using Magento 2.2.4 for online Bookstore. I have books under categories and sub-categories.

I need to list all these categories and sub-categories on Homepage left side like in the Screenshot below.

enter image description here

When clicking on the ctaegory, it should be navigated to the books under relevant category. Since I am fresher to Magento and PHP, I am trying customization using CMS rather code modifications. I tried this with widgets and blocks, but I could not find an effective way. Is there a way to do this?

Best Answer

I have to do something similar and I have used following code to achieve the requirement.

app/code/Anshu/CategoryFilter/registration.php

<?php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Anshu_CategoryFilter',
    __DIR__
);

app/code/Anshu/CategoryFilter/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="Anshu_CategoryFilter" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Catalog"/>
        </sequence>
    </module>
</config>

app/code/Anshu/CategoryFilter/Block/CategoryFilter.php

<?php

namespace Anshu\CategoryFilter\Block;

class CategoryFilter extends \Magento\Framework\View\Element\Template {

    /**
     * @var \Magento\Catalog\Helper\Category
     */
    protected $_categoryHelper;

    /**
     * @var \Magento\Catalog\Model\Indexer\Category\Flat\State
     */
    protected $categoryFlatConfig;

    /**
     * @var \Magento\Catalog\Model\CategoryFactory
     */
    protected $_categoryFactory;

    /**
     * @param Template\Context                                        $context
     * @param \Magento\Catalog\Helper\Category                        $categoryHelper
     * @param \Magento\Catalog\Model\Indexer\Category\Flat\State      $categoryFlatState
     * @param \Magento\Catalog\Model\CategoryFactory                  $categoryFactory
     * @param array                                                   $data
     */
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Magento\Catalog\Helper\Category $categoryHelper,
        \Magento\Catalog\Model\Indexer\Category\Flat\State $categoryFlatState,
        \Magento\Catalog\Model\CategoryFactory $categoryFactory,
        $data = []
    )
    {
        $this->_categoryHelper           = $categoryHelper;
        $this->categoryFlatConfig        = $categoryFlatState;
        $this->_categoryFactory          = $categoryFactory;
        parent::__construct($context, $data);
    }

    /**
     * Get all categories
     *
     * @param bool $sorted
     * @param bool $asCollection
     * @param bool $toLoad
     *
     * @return array|\Magento\Catalog\Model\ResourceModel\Category\Collection|\Magento\Framework\Data\Tree\Node\Collection
     */
    public function getCategories($sorted = false, $asCollection = false, $toLoad = true)
    {
        $cacheKey = sprintf('%d-%d-%d-%d', $this->getSelectedRootCategory(), $sorted, $asCollection, $toLoad);
        if ( isset($this->_storeCategories[ $cacheKey ]) )
        {
            return $this->_storeCategories[ $cacheKey ];
        }
        /**
         * Check if parent node of the store still exists
         */
        $category = $this->_categoryFactory->create();

        $storeCategories = $category->getCategories($this->getSelectedRootCategory(), $recursionLevel = 0, $sorted, $asCollection, $toLoad);
        $this->_storeCategories[ $cacheKey ] = $storeCategories;
        return $storeCategories;
    }

    /**
     * Get current store root category id
     *
     * @return int|mixed
     */
    public function getSelectedRootCategory()
    {
        return $this->_storeManager->getStore()->getRootCategoryId();
    }

    /**
     * @param        $category
     * @param string $html
     * @param int    $level
     *
     * @return string
     */
    public function getChildCategoryView($category, $html = '', $level = 1)
    {   
        // Check if category has children
        if ( $category->hasChildren() )
        {  
            $childCategories = $this->getSubcategories($category);
            $childCount = (int)$category->getChildrenCount();
            if ( $childCount > 0 )
            {   
                $html .= '<ul class="o-list o-list--unstyled" style="display:none">';
                // Loop through children categories
                foreach ( $childCategories as $childCategory )
                {
                    $html .= '<li class="level' . $level . '">';
                    $html .= '<a href="' . $this->getCategoryUrl($childCategory) . '" title="' . $childCategory->getName() . '">' . $childCategory->getName() . '</a>';
                    if ($childCategory->hasChildren())
                    {
                        $html .= '<span class="expand"><i class="fa fa-plus">+/-</i></span>';
                        $html .= $this->getChildCategoryView($childCategory, '', ($level + 1));
                    }

                    $html .= '</li>';
                }
                $html .= '</ul>';
            }
        }
        return $html;
    }

    /**
     * Retrieve subcategories
     * 
     * @param $category
     *
     * @return array
     */ 
    public function getSubcategories($category)
    {
        if ($this->categoryFlatConfig->isFlatEnabled() && $category->getUseFlatResource()) {
            $subcategories = (array)$category->getChildrenNodes();
        } else {
            $subcategories = $category->getChildren();
        }
        return $subcategories;
    }

    /**
     * Return Category URL
     *
     * @param $category
     *
     * @return string
     */
    public function getCategoryUrl($category)
    {
        return $this->_categoryHelper->getCategoryUrl($category);
    }
}

app/code/Anshu/CategoryFilter/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            categoryfilter: 'Anshu_CategoryFilter/js/categoryfilter'
        }
    }
};

app/code/Anshu/CategoryFilter/view/frontend/web/js/categoryfilter.js

require(['jquery'], function ($) {
    $(function () {
        $('.category-heading').on('click', function () {
            $('.category-filter').toggle();
        });
        $('.category-filter').on('click', '.o-list .expand, .o-list .expanded', function () {
            var element = $(this).parent('li');
            if (element.hasClass('active')) {
                element.find('ul:first').slideUp();
                element.removeClass('active');
            } else {
                element.addClass('active');
                element.children('ul').slideDown();
                element.parent('ul').find('i').addClass('fa-plus');
                element.find('> span i').addClass('fa-minus');
            }
        });
    });
});

app/code/Anshu/CategoryFilter/view/frontend/templates/categoryfilter.phtml

<?php
$categories = $block->getCategories();
?>
<div class="category-filter-area">
    <div class="filter-heading">
        <h3><?php echo __('CATEGORY'); ?></h3>
    </div>
    <div class="category-filter">
        <ul class="o-list">
            <?php
            // Loop through categories
            foreach ( $categories as $category ) {
                ?>
                <li class="level0">
                    <a href="<?php echo $block->getCategoryUrl($category); ?>" title="<?php echo $category->getName(); ?>">
                        <?php echo $category->getName(); ?>
                    </a>
                    <?php if ( $category->hasChildren() ) { ?>
                        <span class="expand"><i class="fa fa-plus">+/-</i></span>
                    <?php } ?>

                    <?php echo $block->getChildCategoryView($category, $html = '', $level = $category->getLevel() + 1); ?>
                </li>
            <?php } ?>
        </ul>
    </div>
</div>    
<script type="text/javascript">
    require(['jquery','categoryfilter'], function () {});
</script>

You can modify this code according to your requirement. I hope this will be helpful.

I have to show this block on cms page, so I have called it in CMS page in admin. You can call this or show this block at any place you want.