7

I created a CMS page for products that contain a certain attribute (On Sale Items). I extended the ListProduct class and added filters to the product collection to achieve the desired results. (modified the _getProductCollection method).

I was able to add the toolbar and pager (with accurate counts).

However, I cannot seem to get the layered navigation to work. I added the default layered navigation classes to my CMS page XML.

It shows the layered navigation for the entire catalog but I know that I have to extend the class or classes that render it to show my custom collection.

I have tried extending the product collection classes in layer.php and navigation.php with no luck (added collection filters).

I know that in Magento 1, you can extend the product list and layer classes(located in list.php, view.php, and layer.php if I remember correctly), add the desired filters, and render the custom classes in the CMS page.

I know that people have come across a situation in which they need to create a Clearance, On Sale, or any other CMS page where a custom product collection is used, and needs layered navigation.

Any ideas on how to achieve this in Magento 2?

3 Answers 3

2

I've been trying to achieve the same thing for a long time. I would really like if someone like Amit Bera could help us with this because there are many questions about "how to get layered navigation with custom collection" and not many usefull answers. Maybe this will help you a little and if you do succeed please share your answer.

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 custompage via avaiable special price today. You will have attributes to filter but i still cant get category filter to work properly ( count number for products in collection and layered navigation doesnt match and not all categories are displayed ). If anyone can provide answer on how to make category filter to work with this or other solutions please help.

Most of this code i found here on stackexchange in questions with layered-navigation tag (believe me when i say i searched a lot).

Hope this will get you somewhere. :)

5
  • What will come in FilterList.php file? Nov 15, 2018 at 9:00
  • I have used this solution but getting below error: Uncaught TypeError: Argument 1 passed to Amasty\Shopby\Block\Navigation\FilterRenderer\Interceptor::checkedFilter() must be an instance of Amasty\Shopby\Model\Layer\Filter\Item, instance of Magento\Catalog\Model\Layer\Filter\Item given in /var/www/html/vcs/thelevele/generated/code/Amasty/Shopby/Block/Navigation/FilterRenderer/Interceptor.php:46 Does anybody has solution of this error? Nov 23, 2018 at 10:03
  • It's because you are using Amasty Improved LayeredNavigation module.
    – embed0
    Nov 26, 2018 at 8:08
  • How can fix the category filter issue in layered navigation? I found the collection error message "We can't find products matching the selection." Jan 16, 2019 at 8:48
  • How can I fix Category filter issue? Any changes need in Vendor/Module/Model/Layer/Filter/Category.php ? Jan 16, 2019 at 13:19
1

I created an extension which displays products on special within it's own controller

https://github.com/DominicWatts/Special

I spotted another answer on here did the same although they had issue with the category filter. Category filter seems fine to me on the version I've put together. Although I could be misunderstanding their issue.

Anyway, I hope it helps someone. I'm surprised this isn't in core code. Unless it is and it's not documented. I'm happy to be corrected. Developed on 2.3

8
  • It returns blank page. Can you please tell me how to solve that issue?
    – Niket
    Oct 16, 2019 at 10:20
  • @Niket anything in the logs? Oct 16, 2019 at 11:26
  • Nope. Btw, how to display layered navigation in that ?
    – Niket
    Oct 16, 2019 at 11:41
  • Should I need to setup anything after setup upgrade for remove blank page and display product?
    – Niket
    Oct 16, 2019 at 11:41
  • @Niket Try clearing cache and running DI compile Oct 16, 2019 at 11:43
0

I successfully applied my custom product collection on category page's layered navigation and toolbar.

For example, I am fetching the collection of those products whose price is less than 100.

Step 1: Add below code snippet

app/code/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">

    <type name="Magento\Catalog\Model\Layer">
        <plugin name="custom_product_model_layer" type="Vendor\Module\Plugin\Layer" />
    </type>

    <type name="Magento\Catalog\Block\Product\ProductList\Toolbar">
        <plugin name="custom_product_toolbar" type="Vendor\Module\Plugin\Toolbar" />
    </type>

    <virtualType name="categoryFilterList" type="Magento\Catalog\Model\Layer\FilterList">
        <arguments>
            <argument name="filters" xsi:type="array">
                <item name="attribute" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Attribute</item>
                <item name="price" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Price</item>
                <item name="decimal" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Decimal</item>
                <item name="category" xsi:type="string">Magento\Catalog\Model\Layer\Filter\Category</item>
            </argument>
        </arguments>
    </virtualType>

</config>

Step 2: Create plugin for product collection

app/code/Vendor/Module/Plugin/Layer.php

<?php
namespace Vendor\Module\Plugin;
class Layer
{
  public function aroundGetProductCollection(
    \Magento\Catalog\Model\Layer $subject,
    \Closure $proceed
  ) {

    $result = $proceed();
    $result->addAttributeToFilter('price', array('lt' => 100));
    return $result;
  }
}

Step 3: Create plugin for toolbar

app/code/Vendor/Module/Plugin/Toolbar.php

<?php
namespace Vendor\Module\Plugin;
class Toolbar
{

  protected $_objectManager;
  protected $request;

  public function __construct(
    \Magento\Framework\ObjectManagerInterface $objectmanager,
    \Magento\Framework\App\Request\Http $request
  ) {
    $this->_objectManager = $objectmanager;
    $this->request = $request;
  }

  public function aroundSetCollection(
    \Magento\Catalog\Block\Product\ProductList\Toolbar $subject,
    \Closure $proceed,
    $request
  ) {
    $result = $proceed($request);

    $this->_collection = $request;
    $category = $this->_objectManager->get('Magento\Framework\Registry')->registry('current_category');
    if($category)
    {
      $page = $this->request->getParam('p');
      if($page == '')
      {
        $page = 1;
      }
      $this->_collection->getCurPage();
      $this->_collection->setCurPage($page);  
    }
    //else
    //{
    //  $this->_collection->setCurPage($this->getCurrentPage());
    //}

    return $result;
  }

}
2
  • This is working but filter is not appying in magento 2.2 Dec 18, 2018 at 7:20
  • Hi Yasar, Did u get any solution? because for me also the same issue. Jun 1, 2019 at 5:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.