How to create layered navigation on custom page in Magento 2?

Created by Rosa Truong, Modified on Mon, 23 Dec, 2019 at 9:44 AM by Rosa Truong

Link module: https://github.com/BssGroup/HelloWorld 

First, create di.xml with following code under Bss/HelloWorld/etc/ folder,

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/ObjectManager/etc/config.xsd">
    <preference for="Magento\Catalog\Model\Layer\ContextInterface" type="Magento\Catalog\Model\Layer\Context" />
    <preference for="Magento\Catalog\Model\Layer\ItemCollectionProviderInterface" type="Magento\Catalog\Model\Layer\Category\ItemCollectionProvider" />
    <preference for="Magento\Catalog\Model\Layer\StateKeyInterface" type="Magento\Catalog\Model\Layer\Category\StateKey" />
    <preference for="Magento\Catalog\Model\Layer\CollectionFilterInterface" type="Magento\Catalog\Model\Layer\Category\CollectionFilter" />
    <preference for="Magento\Catalog\Model\Layer\FilterableAttributeListInterface" type="Magento\Catalog\Model\Layer\Category\FilterableAttributeList" />
    <preference for="Magento\Catalog\Model\Layer\AvailabilityFlagInterface" type="Magento\Catalog\Model\Layer\Category\AvailabilityFlag" />
    <preference for="Magento\Catalog\Model\ResourceModel\Layer\Filter\Price" type="Bss\HelloWorld\Model\ResourceModel\Layer\Filter\Price" />
</config>

Then add following in the layout file which in this case is helloworld_index_index.xml under Bss/HelloWorld/view/frontend/layout/ folder,

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">

    <head>
        <title>Hello World</title>
    </head>
    <body>
        <referenceContainer name="content">
            <block class="Bss\HelloWorld\Block\Product\ListProduct" name="custom.products.list" as="product_list" template="Magento_Catalog::product/list.phtml">
                <container name="category.product.list.additional" as="additional" />
                <block class="Magento\Framework\View\Element\RendererList" name="category.product.type.details.renderers" as="details.renderers">
                    <block class="Magento\Framework\View\Element\Template" as="default"/>
                </block>
                <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="category.product.addto" as="addto">
                    <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare"
                           name="category.product.addto.compare" as="compare"
                           template="Magento_Catalog::product/list/addto/compare.phtml"/>
                </block>
                <block class="Magento\Catalog\Block\Product\ProductList\Toolbar" name="product_list_toolbar" template="Magento_Catalog::product/list/toolbar.phtml">

                    <block class="Magento\Theme\Block\Html\Pager" name="product_list_toolbar_pager"/>

                </block>
                <action method="setToolbarBlockName">
                    <argument name="name" xsi:type="string">product_list_toolbar</argument>
                </action>
            </block>
        </referenceContainer>
        <referenceContainer name="sidebar.main">
            <block class="Bss\HelloWorld\Block\Navigation" name="catalog.leftnav" as="navigation" before="-" template="Magento_LayeredNavigation::layer/view.phtml">
                <block class="Bss\HelloWorld\Block\Navigation\State" name="catalog.navigation.state" as="state" template="Magento_LayeredNavigation::layer/state.phtml" />
                <block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalog.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"/>
            </block>
        </referenceContainer>
    </body>
</page>

 

In the above code, we have added class page-products to the body tag to inherit the default styling.

Now we will override the model files to get the required data.

Now create Layer.php under Bss/HelloWorld/Model/ folder with following code,

<?php
namespace Bss\HelloWorld\Model;
 
use Magento\Catalog\Api\CategoryRepositoryInterface;
use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory as AttributeCollectionFactory;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
 
class Layer extends \Magento\Catalog\Model\Layer
{
    public function __construct(
        \Magento\Catalog\Model\Layer\ContextInterface $context,
        \Magento\Catalog\Model\Layer\StateFactory $layerStateFactory,
        AttributeCollectionFactory $attributeCollectionFactory,
        \Magento\Catalog\Model\ResourceModel\Product $catalogProduct,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\Registry $registry,
        CategoryRepositoryInterface $categoryRepository,
        CollectionFactory $productCollectionFactory,
        array $data = []
    ) {
        $this->productCollectionFactory = $productCollectionFactory;
        parent::__construct(
            $context,
            $layerStateFactory,
            $attributeCollectionFactory,
            $catalogProduct,
            $storeManager,
            $registry,
            $categoryRepository,
            $data
        );
    }
 
}

After that create Resolver.php under Bss/HelloWorld/Model/Layer/ folder with following code,

<?php
namespace Bss\HelloWorld\Model\Layer;
 
class Resolver extends \Magento\Catalog\Model\Layer\Resolver
{
    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectManager,
        \Bss\HelloWorld\Model\Layer $layer,
        array $layersPool
    ) {
        $this->layer = $layer;
        parent::__construct($objectManager, $layersPool);
    }
 
    public function create($layerType)
    {
        
    }
}

Then under Bss/HelloWorld/Model/ResourceModel/Layer/Filter/ folder create Price.php file,

<?php
namespace Bss\HelloWorld\Model\ResourceModel\Layer\Filter;
 
use Magento\Framework\App\Http\Context;
use Magento\Framework\Indexer\DimensionFactory;
use Magento\Framework\Search\Request\IndexScopeResolverInterface;
 
class Price extends \Magento\Catalog\Model\ResourceModel\Layer\Filter\Price 
{
    public function __construct(
        \Magento\Framework\Model\ResourceModel\Db\Context $context,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Bss\HelloWorld\Model\Layer\Resolver $layerResolver,
        \Magento\Customer\Model\Session $session,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        $connectionName = null,
        IndexScopeResolverInterface $priceTableResolver = null,
        Context $httpContext = null,
        DimensionFactory $dimensionFactory = null
    ) {
        parent::__construct($context, $eventManager, $layerResolver, $session, $storeManager, $connectionName, $priceTableResolver, $httpContext, $dimensionFactory);
    }
}

Now we are done with the model and we will override the block to show proper data.

Now create Navigation.php file under Bss/HelloWorld/Block/ folder with the following content,

<?php
namespace Bss\HelloWorld\Block;
 
class Navigation extends \Magento\LayeredNavigation\Block\Navigation
{
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Bss\HelloWorld\Model\Layer\Resolver $layerResolver,
        \Magento\Catalog\Model\Layer\FilterList $filterList,
        \Magento\Catalog\Model\Layer\AvailabilityFlagInterface $visibilityFlag,
        array $data = []
    ) {
        parent::__construct($context, $layerResolver, $filterList,
            $visibilityFlag);
    }
}

Then create State.php file under Bss/HelloWorld/Block/Navigation/ folder with following code,

<?php
namespace Bss\HelloWorld\Block\Navigation;
 
class State extends \Magento\LayeredNavigation\Block\Navigation\State
{
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Bss\HelloWorld\Model\Layer\Resolver $layerResolver,
        array $data = []
    ) {
        parent::__construct($context, $layerResolver, $data);
}
}

Then under Bss/HelloWorld/Block/Product/ folder create ListProduct.php and add following code,

<?php
namespace Bss\HelloWorld\Block\Product;
 
class ListProduct extends \Magento\Catalog\Block\Product\ListProduct
{
    public function __construct(
        \Magento\Catalog\Block\Product\Context $context,
        \Magento\Framework\Data\Helper\PostHelper $postDataHelper,
        \Bss\HelloWorld\Model\Layer\Resolver $layerResolver,
        \Magento\Catalog\Api\CategoryRepositoryInterface $categoryRepository,
        \Magento\Framework\Url\Helper\Data $urlHelper,
        array $data = []
    ) {
        parent::__construct($context, $postDataHelper, $layerResolver,
            $categoryRepository, $urlHelper, $data);
    }
}

That’s it, we have overridden all the required files.

Now you can check the front-end and it should display the layered navigation on the left side


Please feel free to contact us if you need any assistance at sales@bsscommerce.com 

Was this article helpful?

That’s Great!

Thank you for your feedback

Sorry! We couldn't be helpful

Thank you for your feedback

Let us know how can we improve this article!

Select at least one of the reasons
CAPTCHA verification is required.

Feedback sent

We appreciate your effort and will try to fix the article