Magento 2.1.8 layered navigation bug fix

The Problem

We done a Magento upgrade from 2.1.7 to 2.1.8 version. Soon after that the layered navigation was broken. More specifically the filters on cataloge were not working. Counter besides the custom attributes were showing the correct number. Everything looked fine, but soon we realized when you click on one filter you would get no result.

Finding possible solutions

It took us quite some time to find the right solution for this. We tried this and that and every possible solution we could find out there. Every solution took from 15 minutes up to 2 or 3 hours to try it out. Everything made sense but nothing worked. We tried reindexing, rebuilding, cleaning cache, changing core Magento files, looking into our database in hopes of finding something. We thought something was missing so we even tried creating new custom attributes and test it but with no luck. There were quite a lot of “solutions” and open threads on github.

Working solution

So we run out of options and decided to give up on finding a solution. We hoped that in the next Magento update the layered navigation will be fixed. Or at least there will be a guideline how to fix it for those who updated Magento from 2.1.7 to 2.1.8. We switched our focus on developing new features while waiting for the fix. The next day I was on lunch break when I received a message from my boss “found the solution :D”. I was like ok I don’t believe it till i see it with my own eyes. And he sends me a link to a 4 MONTH old github commit but with a 23h old comment saying it solved the EXACT same problem we have.

Thank you. Applying this change fixed our layered navigation which had broken when we upgraded from 2.1.7 to 2.1.8.

Would recommend this change to anyone with a similar issue

You can see the commit here.

Short:

Create or edit

app/code/Magento/CatalogSearch/Model/Search/FilterMapper/TermDropdownStrategy.php

Paste the following:

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Magento\CatalogSearch\Model\Search\FilterMapper;

use Magento\CatalogSearch\Model\Adapter\Mysql\Filter\AliasResolver;
use Magento\Eav\Model\Config as EavConfig;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\ResourceConnection;
use Magento\Store\Model\ScopeInterface;
use Magento\Store\Model\StoreManagerInterface;
use Magento\Framework\App\ObjectManager;

/**
 * This strategy handles attributes which comply with two criteria:
 *   - The filter for dropdown or multi-select attribute
 *   - The filter is Term filter
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */
class TermDropdownStrategy implements FilterStrategyInterface
{
    /**
     * @var AliasResolver
     */
    private $aliasResolver;

    /**
     * @var StoreManagerInterface
     */
    private $storeManager;

    /**
     * @var EavConfig
     */
    private $eavConfig;

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;

    /**
     * @var ScopeConfigInterface
     */
    private $scopeConfig;

    /**
     * @var \Magento\Indexer\Model\ResourceModel\FrontendResource
     */
    private $frontendResource;

    /**
     * @var \Magento\Indexer\Model\ResourceModel\FrontendResource
     */
    private $indexerStockFrontendResource;

    /**
     * @param StoreManagerInterface $storeManager
     * @param ResourceConnection $resourceConnection
     * @param EavConfig $eavConfig
     * @param ScopeConfigInterface $scopeConfig
     * @param AliasResolver $aliasResolver
     * @param \Magento\Indexer\Model\ResourceModel\FrontendResource|null $frontendResource
     * @param null|\Magento\Indexer\Model\ResourceModel\FrontendResource $indexerStockFrontendResource
     * @SuppressWarnings(Magento.TypeDuplication)
     */
    public function __construct(
        StoreManagerInterface $storeManager,
        ResourceConnection $resourceConnection,
        EavConfig $eavConfig,
        ScopeConfigInterface $scopeConfig,
        AliasResolver $aliasResolver,
        \Magento\Indexer\Model\ResourceModel\FrontendResource $frontendResource = null,
        \Magento\Indexer\Model\ResourceModel\FrontendResource $indexerStockFrontendResource = null
    ) {
        $this->storeManager = $storeManager;
        $this->resourceConnection = $resourceConnection;
        $this->eavConfig = $eavConfig;
        $this->scopeConfig = $scopeConfig;
        $this->aliasResolver = $aliasResolver;
        $this->frontendResource = $frontendResource ?: ObjectManager::getInstance()
            ->get(\Magento\Catalog\Model\ResourceModel\Product\Indexer\Eav\FrontendResource::class);
        $this->indexerStockFrontendResource = $indexerStockFrontendResource ?: ObjectManager::getInstance()
            ->get(\Magento\CatalogInventory\Model\ResourceModel\Indexer\Stock\FrontendResource::class);
    }

    /**
     * {@inheritDoc}
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function apply(
        \Magento\Framework\Search\Request\FilterInterface $filter,
        \Magento\Framework\DB\Select $select
    ) {
        $alias = $this->aliasResolver->getAlias($filter);
        $attribute = $this->getAttributeByCode($filter->getField());
        $joinCondition = sprintf(
            'search_index.entity_id = %1$s.entity_id AND %1$s.attribute_id = %2$d AND %1$s.store_id = %3$d',
            $alias,
            $attribute->getId(),
            $this->storeManager->getStore()->getId()
        );
        $select->joinLeft(
            [$alias => $this->frontendResource->getMainTable()],
            $joinCondition,
            []
        );
        if ($this->isAddStockFilter()) {
            $stockAlias = $alias . AliasResolver::STOCK_FILTER_SUFFIX;
            $select->joinLeft(
                [
                    $stockAlias => $this->indexerStockFrontendResource->getMainTable(),
                ],
                sprintf('%2$s.product_id = %1$s.source_id', $alias, $stockAlias),
                []
            );
        }

        return true;
    }

    /**
     * @param string $field
     * @return \Magento\Catalog\Model\ResourceModel\Eav\Attribute
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function getAttributeByCode($field)
    {
        return $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $field);
    }

    /**
     * @return bool
     */
    private function isAddStockFilter()
    {
        $isShowOutOfStock = $this->scopeConfig->isSetFlag(
            'cataloginventory/options/show_out_of_stock',
            ScopeInterface::SCOPE_STORE
        );

        return false === $isShowOutOfStock;
    }
}

Raw code here

Leave a Reply

Your email address will not be published. Required fields are marked *