I want to add a layered navigation block for a custom product collection. Is there a way to do that in Magento2 ?
3 Answers
I know you can add one manually by adding a static block (with links to all the items you want to link) and calling it in the category layout and update xml using the code below. P.S. "Block_Code" is your static block code.
<referenceContainer name="sidebar.additional">
<block class="Magento\Cms\Block\Block" name="male_model">
<arguments>
<argument name="block_id" xsi:type="string">Block_Code</argument>
</arguments>
</block>
</referenceContainer>
in admin go to : Stores > Attributes > Product
Find the attribute you want to use and set "Use in Layered Navigation" to one of the filterable options
In catalog_category_view.xml
<referenceContainer name="content">
<block class="Magento\Catalog\Block\Category\View" name="category.products" template="Magento_Catalog::category/products.phtml">
<block class="Magento\LayeredNavigation\Block\Navigation\Category" name="catalog.leftnav" before="-" template="Magento_LayeredNavigation::layer/view.phtml"/>
</block>
</referenceContainer>
In category/products.phtml
<?php echo $block->getChildHtml('catalog.leftnav'); ?>
I was able to get product collection by using layerResolver like this:
Vendor/Module/Controller/Index/Index.php
public function __construct(
\Magento\Framework\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $pageFactory,
\Magento\Catalog\Model\Layer\Resolver $layerResolver
) {
$this->layerResolver = $layerResolver;
$this->pageFactory = $pageFactory;
$this->context = $context;
parent::__construct($context);
}
public function execute()
{
$date = new \Zend_Date();
$result = $this->pageFactory->create();
$this->layerResolver->create('search');
$collection = $this->layerResolver->get()->getProductCollection();
$collection->('special_price', ['gt'=>0],'left');
$collection
->addAttributeToFilter(
'special_from_date',
[
'or' => [
0 => [
'date' => true,
'to' => $date->get('YYYY-MM-dd').' 23:59:59'],
1 => [
'is' => new \Zend_Db_Expr('null')
],
]
],
'left'
)->addAttributeToFilter(
'special_to_date',
[
'or' => [
0 => [
'date' => true,
'from' => $date->get('YYYY-MM-dd').' 00:00:00'],
1 => [
'is' => new \Zend_Db_Expr('null')
],
]
],
'left'
);
$list = $result->getLayout()->getBlock('custom.products.list');
$list->setProductCollection($collection);
return $result;
}
To avoid problems with aggregations and buckets I use this:
Vendor/Module/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">
<!-- To prepare the filterlist for our custom collection which would be passed to the left navigation we need below virtual types for our custom page navigation -->
<virtualType name="customFilterList" type="Vendor\Module\Model\Layer\FilterList">
<arguments>
<argument name="filterableAttributes" xsi:type="object">Vendor\Module\Model\Layer\FilterableAttributeList</argument>
<argument name="filters" xsi:type="array">
<item name="attribute" xsi:type="string">Vendor\Module\Model\Layer\Filter\Attribute</item>
<item name="category" xsi:type="string">Vendor\Module\Model\Layer\Filter\Category</item>
</argument>
</arguments>
</virtualType>
<!-- once the filter list virtual type is ready we can pass the same to our navigation , I have prepared the virtual type of the core navigation for my custom module and have passed the custom filter list to it -->
<virtualType name="Vendor\Module\Block\Navigation\Custnavigation" type="Magento\LayeredNavigation\Block\Navigation">
<arguments>
<argument name="filterList" xsi:type="object">customFilterList</argument>
</arguments>
</virtualType>
</config>
Vendor/Module/Model/Layer/FilterableAttributeList.php
namespace Vendor\Module\Model\Layer;
class FilterableAttributeList extends \Magento\Catalog\Model\Layer\Category\FilterableAttributeList
{
}
Vendor/Module/Model/Layer/FilterList.php
namespace Vendor\Module\Model\Layer;
class FilterList extends \Magento\Catalog\Model\Layer\FilterList
{
}
Vendor/Module/Model/Layer/Filter/Attribute.php
namespace Vendor\Module\Model\Layer\Filter;
class Attribute extends \Magento\Catalog\Model\Layer\Filter\Attribute
{
}
Vendor/Module/Model/Layer/Filter/Category.php
namespace Vendor\Module\Model\Layer\Filter;
class Category extends \Magento\CatalogSearch\Model\Layer\Filter\Category
{
}
Vendor/Module/view/frontend/layout/custompage_index_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left">
<body>
<attribute name="class" value="page-with-filter"/>
<referenceContainer name="sidebar.main">
<block class="Vendor\Module\Block\Navigation\Custnavigation" name="catalog.leftnav" before="-" template="Magento_LayeredNavigation::layer/view.phtml">
<block class="Magento\LayeredNavigation\Block\Navigation\State" name="catalog.navigation.state" as="state" />
<block class="Magento\LayeredNavigation\Block\Navigation\FilterRenderer" name="catalog.navigation.renderer" as="renderer" template="Magento_LayeredNavigation::layer/filter.phtml"/>
</block>
</referenceContainer>
<referenceContainer name="content">
<block class="Vendor\Module\Block\Product\CustomList" 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\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>
</body>
</page>
Vendor/Module/Block/Product/CustomList.php
namespace Vendor\Module\Block\Product;
use Magento\Catalog\Block\Product\ListProduct;
use Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection;
class CustomList extends ListProduct
{
public function getLoadedProductCollection()
{
return $this->_productCollection;
}
public function setProductCollection(AbstractCollection $collection)
{
$this->_productCollection = $collection;
}
}
This will give you filtered collection on custom page via available special price today.
Most of this code was found here on Stack Exchange in questions with layered-navigation tag.