Today I’ll explain how you can add layered navigation to a custom page. In this example I’ll create a module named Webkul_LayeredNavigation and on layerednavigation/index/index page I’ll show the products with layered navigation.
Here I’ll only explain those code which are necessary for implementation of the layered navigation.
P.S – You can check Magento 2 Layered Navigation Extension
First create di.xml with following code under Webkul/LayeredNavigation/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="Webkul\LayeredNavigation\Model\ResourceModel\Layer\Filter\Price" /> </config>
Then add following in the layout file which in my case is layerednavigation_index_index.xml under Webkul/LayeredNavigation/view/frontend/layout/ folder,
<?xml version="1.0"?> <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd"> <body> <attribute name="class" value="page-products"/> <referenceContainer name="content"> <block class="Webkul\LayeredNavigation\Block\Product\ListProduct" name="layerednavigation_index_index" 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" name="category.product.type.details.renderers.default" 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="Webkul\LayeredNavigation\Block\Navigation" name="catalog.leftnav" as="navigation" before="-" template="Magento_LayeredNavigation::layer/view.phtml"> <block class="Webkul\LayeredNavigation\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"> <arguments> <argument name="product_layer_view_model" xsi:type="object">Magento\LayeredNavigation\ViewModel\Layer\Filter</argument> </arguments> </block> </block> </referenceContainer> <referenceBlock name="layerednavigation_index_index"> <arguments> <argument name="viewModel" xsi:type="object">Magento\Catalog\ViewModel\Product\OptionsData</argument> </arguments> </referenceBlock> </body> </page>
In the above code I have added class page-products to the body tag to inherit the default styling.
Now we will override the model files to get required data.
Now create Layer.php under Webkul/LayeredNavigation/Model/ folder with following code,
<?php namespace Webkul\LayeredNavigation\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 ); } public function getProductCollection() { if (isset($this->_productCollections['webkul_custom'])) { $collection = $this->_productCollections['webkul_custom']; } else { //here you assign your own custom collection of products $collection = $this->productCollectionFactory->create(); $this->prepareProductCollection($collection); $this->_productCollections['webkul_custom'] = $collection; } return $collection; } }
After that create Resolver.php under Webkul/LayeredNavigation/Model/Layer/ folder with following code,
<?php namespace Webkul\LayeredNavigation\Model\Layer; class Resolver extends \Magento\Catalog\Model\Layer\Resolver { public function __construct( \Magento\Framework\ObjectManagerInterface $objectManager, \Webkul\LayeredNavigation\Model\Layer $layer, array $layersPool ) { $this->layer = $layer; parent::__construct($objectManager, $layersPool); } public function create($layerType) { } }
Then under Webkul/LayeredNavigation/Model/ResourceModel/Layer/Filter/ folder create Price.php file,
<?php namespace Webkul\LayeredNavigation\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, \Webkul\LayeredNavigation\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 Webkul/LayeredNavigation/Block/ folder with following content,
<?php namespace Webkul\LayeredNavigation\Block; class Navigation extends \Magento\LayeredNavigation\Block\Navigation { public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Webkul\LayeredNavigation\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 Webkul/LayeredNavigation/Block/Navigation/ folder with following code,
<?php namespace Webkul\LayeredNavigation\Block\Navigation; class State extends \Magento\LayeredNavigation\Block\Navigation\State { public function __construct( \Magento\Framework\View\Element\Template\Context $context, \Webkul\LayeredNavigation\Model\Layer\Resolver $layerResolver, array $data = [] ) { parent::__construct($context, $layerResolver, $data); } }
Then under Webkul/LayeredNavigation/Block/Product/ folder create ListProduct.php and add following code,
<?php namespace Webkul\LayeredNavigation\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, \Webkul\LayeredNavigation\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 left side,
Happy coding 🙂
This is very useful!
I’m having an issue with price filter on this custom layered navigation. Query is broken when I activate filtering by price. I see magento’s ORM is mixing wrong tables. Take a look this pastebin https://pastebin.com/3RCb3Byj
Any help will be appreciated.
Thanks!