4

In order to add a custom filter to the Layered Navigation, I overrode the class Magento\LayeredNavigation\Block\Navigation\Category to load my own FilterList class instead of the one from core.

My custom FilterList simply adds another step when determining which Filter class to apply. The custom Filter class simply applies a stepping of 4 on a decimal attribute (instead of the standard steppings).

But, as soon as I use this custom FilterList class instead of the one from the Magento 2 module, the Layered Navigation always shows the pagination options as well as the wrong Collection size. The filtering is done correctly (only the filtered products show up), but also the pagination shows with the wrong size:

Wrong Collection size leads to always shown pagination.

There is a total of 13 products in this category, but the Collection that is used to determine the pagination always has a size of 13, no matter what filter I activate.

This error does not occur when I load the default FilterList class in Magento\LayeredNavigation\Block\Navigation\Category.

The custom FilterList class:

class FilterList extends \Magento\Catalog\Model\Layer\FilterList
{

    const CUSTOM_FILTER_STRING  = 'customString';

    protected $filterTypes = [
        self::CATEGORY_FILTER  => \Magento\Catalog\Model\Layer\Filter\Category::class,
        self::ATTRIBUTE_FILTER => \Magento\Catalog\Model\Layer\Filter\Attribute::class,
        self::PRICE_FILTER     => \Magento\Catalog\Model\Layer\Filter\Price::class,
        self::DECIMAL_FILTER   => \Magento\Catalog\Model\Layer\Filter\Decimal::class,
        self::CUSTOM_FILTER    => \Vendor\Module\Model\Layer\Filter\CustomFilter::class
    ];

    protected function getAttributeFilterClass(\Magento\Catalog\Model\ResourceModel\Eav\Attribute $attribute)
    {
        $filterClassName = $this->filterTypes[self::ATTRIBUTE_FILTER];

        if ($attribute->getAttributeCode() == 'price') {
            $filterClassName = $this->filterTypes[self::PRICE_FILTER];
        } elseif (substr($attribute->getAttributeCode(), 0, 9) == 'customStr') {
            $filterClassName = $this->filterTypes[self::CUSTOM_FILTER];
        } elseif ($attribute->getBackendType() == 'decimal') {
            $filterClassName = $this->filterTypes[self::DECIMAL_FILTER];
        }

        return $filterClassName;
    }
}

The etc/frontend/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">
    <virtualType name="Magento\LayeredNavigation\Block\Navigation\Category" type="Vendor\Module\Block\LayeredNavigation\Navigation">
        <arguments>
            <argument name="filterList" xsi:type="object">categoryFilterList</argument>
        </arguments>
    </virtualType>

    <!-- Override the Interface class to avoid an Interface Instantiation error from Magento.  -->
    <preference for="Magento\Catalog\Model\Layer\FilterableAttributeListInterface" type="Magento\Catalog\Model\Layer\Category\FilterableAttributeList"/>
</config>

1 Answer 1

1

I managed to find a workaround for this problem.

I extend the \Magento\Catalog\Block\Product\ProductList\Toolbar class to overwrite the setCollection() function. By adding the call

$this->getCollection()->getSize();

at the end of the function, before the return, somehow the Collection used in the toolbar.phtml template (which shows the pagination if available) returns the correct size.

The modified \Magento\Catalog\Block\Product\ProductList\Toolbar looks like this:

Vendor\Module\Block\Product\ProductList\Toolbar

namespace Vendor\Module\Block\Product\ProductList;

use Magento\Catalog\Helper\Product\ProductList;
use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel;

class Toolbar extends \Magento\Catalog\Block\Product\ProductList\Toolbar
{
    /**
     * Products collection
     *
     * @var \Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection
     */
    protected $_collection = null;

    /**
     * List of available order fields
     *
     * @var array
     */
    protected $_availableOrder = null;

    /**
     * List of available view types
     *
     * @var array
     */
    protected $_availableMode = [];

    /**
     * Is enable View switcher
     *
     * @var bool
     */
    protected $_enableViewSwitcher = true;

    /**
     * Is Expanded
     *
     * @var bool
     */
    protected $_isExpanded = true;

    /**
     * Default Order field
     *
     * @var string
     */
    protected $_orderField = null;

    /**
     * Default direction
     *
     * @var string
     */
    protected $_direction = ProductList::DEFAULT_SORT_DIRECTION;

    /**
     * Default View mode
     *
     * @var string
     */
    protected $_viewMode = null;

    /**
     * @var bool $_paramsMemorizeAllowed
     */
    protected $_paramsMemorizeAllowed = true;

    /**
     * @var string
     */
    protected $_template = 'Magento_Catalog::product/list/toolbar.phtml';

    /**
     * Catalog config
     *
     * @var \Magento\Catalog\Model\Config
     */
    protected $_catalogConfig;

    /**
     * Catalog session
     *
     * @var \Magento\Catalog\Model\Session
     */
    protected $_catalogSession;

    /**
     * @var ToolbarModel
     */
    protected $_toolbarModel;

    /**
     * @var ProductList
     */
    protected $_productListHelper;

    /**
     * @var \Magento\Framework\Url\EncoderInterface
     */
    protected $urlEncoder;

    /**
     * @var \Magento\Framework\Data\Helper\PostHelper
     */
    protected $_postDataHelper;

    /**
     * @param \Magento\Framework\View\Element\Template\Context $context
     * @param \Magento\Catalog\Model\Session $catalogSession
     * @param \Magento\Catalog\Model\Config $catalogConfig
     * @param ToolbarModel $toolbarModel
     * @param \Magento\Framework\Url\EncoderInterface $urlEncoder
     * @param ProductList $productListHelper
     * @param \Magento\Framework\Data\Helper\PostHelper $postDataHelper
     * @param array $data
     */
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        \Magento\Catalog\Model\Session $catalogSession,
        \Magento\Catalog\Model\Config $catalogConfig,
        ToolbarModel $toolbarModel,
        \Magento\Framework\Url\EncoderInterface $urlEncoder,
        ProductList $productListHelper,
        \Magento\Framework\Data\Helper\PostHelper $postDataHelper,
        array $data = []
    ) {
        $this->_catalogSession = $catalogSession;
        $this->_catalogConfig = $catalogConfig;
        $this->_toolbarModel = $toolbarModel;
        $this->urlEncoder = $urlEncoder;
        $this->_productListHelper = $productListHelper;
        $this->_postDataHelper = $postDataHelper;
        parent::__construct(
            $context,
            $catalogSession,
            $catalogConfig,
            $toolbarModel,
            $urlEncoder,
            $productListHelper,
            $postDataHelper,
            $data
        );
    }

    /**
     * Set collection to pager
     *
     * @param \Magento\Framework\Data\Collection $collection
     * @return $this
     */
    public function setCollection($collection) {
        $this->_collection = $collection;

        $this->_collection->setCurPage($this->getCurrentPage());

        // we need to set pagination only if passed value integer and more that 0
        $limit = (int)$this->getLimit();
        if ($limit) {
            $this->_collection->setPageSize($limit);
        }

        if ($this->getCurrentOrder()) {
            switch ($this->getCurrentOrder()) {
                case 'created_at':
                    if ( $this->getCurrentDirection() == 'desc' ) {
                        $this->_collection
                            ->getSelect()
                            ->order('e.created_at DESC');
                    } elseif ( $this->getCurrentDirection() == 'asc' ) {
                        $this->_collection
                            ->getSelect()
                            ->order('e.created_at ASC');
                    }
                    break;
                default:
                    $this->_collection->setOrder($this->getCurrentOrder(), $this->getCurrentDirection());
                    break;
            }
        }
        // IMPORTANT DO NOT TOUCH! WORKAROUND FOR WRONG COLLECTION SIZE BUG
        $this->getCollection()->getSize();
        return $this;
    }
}

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.