5

I need to update category page collection by filtering restricted SKUs. As like

$collection->getSelect()->where("`e`.`sku` IN('24-MB04', '24-MB03', '24-MB02')");

Above query gives collection exatly what i need. But it won't update pagination block and layered navigation in category page

How to achieve this thing in Magento 2


I have tried to achieve these by Plugin, below are my code files

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">    

    <type name="Magento\Catalog\Block\Product\ListProduct">
        <plugin name="mynamespace-myextension-block-product-list"
                type="Mynamespace\Myextension\Plugin\ProductList"
                sortOrder="10" />
    </type> 
</config>

And Plugin/ProductList.php

<?php
namespace Mynamespace\Myextension\Plugin;

use Magento\Framework\App\ObjectManager;
use Magento\Framework\Search\Request\EmptyRequestDataException;

class ProductList
{

    protected $scopeConfig;

    protected $_objectManager;

    protected $_catalogLayer;

    public function __construct(        
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\ObjectManagerInterface $objectManager,
        \Magento\Catalog\Model\Layer\Resolver $layerResolver
        ) 
    {
        $this->scopeConfig = $scopeConfig;      
        $this->_objectManager = $objectManager;
        $this->_catalogLayer = $layerResolver->get();
    }

    public function beforeToHtml(
        \Magento\Catalog\Block\Product\ListProduct $subject
        )
    {   
        $skus = array('24-MB04', '24-MB03', '24-MB02');

        $pcollection = $this->_catalogLayer->getProductCollection()->addAttributeToFilter('sku', array('in' => $skus));

        $pcollection->getSize();
        $subject->setCollection($pcollection);      
    }
}

Please advise what I am missing

3
  • anyone have idea on magento 2.4 elastic search?
    – sivakumar
    Commented Jun 4, 2021 at 5:53
  • @sivakumar I am facing same issue, have you found anything?
    – Yogita
    Commented Jun 9, 2021 at 14:22
  • @Nalin any luck for Magento 2.4.3? Commented Mar 2, 2022 at 11:18

5 Answers 5

7

I have found answer to my question

To apply custom filter in category, please do as below

1] create custom SKU filter with the help of search_request.xml

create search_request.xml file in MyNamespace/MyModule/etc/search_request.xml

<?xml version="1.0"?>

<requests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_request.xsd">

    <request query="catalog_view_container" index="catalogsearch_fulltext">        
        <queries>
            <query xsi:type="boolQuery" name="catalog_view_container" boost="1">                
                <queryReference clause="should" ref="sku_query"/>
            </query>
            <query name="sku_query" xsi:type="filteredQuery">
                <filterReference clause="must" ref="sku_query_filter"/>
            </query>
        </queries>
        <filters>
            <filter xsi:type="termFilter" name="sku_query_filter" field="sku" value="$sku$"/>
        </filters>

        <from>0</from>
        <size>10000</size>
    </request>
</requests>

2] Override \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection in to your module

=> MyNamespace/MyModule/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">

    ...............

    <preference for="Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection" type="MyNamespace\MyModule\Model\ResourceModel\Fulltext\Collection" />

    ...............

</config>

=> MyNamespace\MyModule\Model\ResourceModel\Fulltext\Collection.php

<?php
namespace MyNamespace\MyModule\Model\ResourceModel\Fulltext;

use Magento\CatalogSearch\Model\Search\RequestGenerator;
use Magento\Framework\DB\Select;
use Magento\Framework\Exception\StateException;
use Magento\Framework\Search\Adapter\Mysql\TemporaryStorage;
use Magento\Framework\Search\Response\QueryResponse;
use Magento\Framework\Search\Request\EmptyRequestDataException;
use Magento\Framework\Search\Request\NonExistingRequestNameException;
use Magento\Framework\Api\Search\SearchResultFactory;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\App\ObjectManager;

/**
 * Fulltext Collection
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 */
class Collection extends \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection
{   
    /**
     * @var  QueryResponse
     * @deprecated
     */
    protected $queryResponse;

    /**
     * Catalog search data
     *
     * @var \Magento\Search\Model\QueryFactory
     * @deprecated
     */
    protected $queryFactory = null;

    /**
     * @var \Magento\Framework\Search\Request\Builder
     * @deprecated
     */
    private $requestBuilder;

    /**
     * @var \Magento\Search\Model\SearchEngine
     * @deprecated
     */
    private $searchEngine;

    /**
     * @var string
     */
    private $queryText;

    /**
     * @var string|null
     */
    private $order = null;

    /**
     * @var string
     */
    private $searchRequestName;

    /**
     * @var \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory
     */
    private $temporaryStorageFactory;

    /**
     * @var \Magento\Search\Api\SearchInterface
     */
    private $search;

    /**
     * @var \Magento\Framework\Api\Search\SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;

    /**
     * @var \Magento\Framework\Api\Search\SearchResultInterface
     */
    private $searchResult;

    /**
     * @var SearchResultFactory
     */
    private $searchResultFactory;

    /**
     * @var \Magento\Framework\Api\FilterBuilder
     */
    private $filterBuilder;

    /**
     * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
     * @param \Psr\Log\LoggerInterface $logger
     * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
     * @param \Magento\Framework\Event\ManagerInterface $eventManager
     * @param \Magento\Eav\Model\Config $eavConfig
     * @param \Magento\Framework\App\ResourceConnection $resource
     * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory
     * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper
     * @param \Magento\Framework\Validator\UniversalFactory $universalFactory
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Framework\Module\Manager $moduleManager
     * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory
     * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl
     * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Framework\Stdlib\DateTime $dateTime
     * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement
     * @param \Magento\Search\Model\QueryFactory $catalogSearchData
     * @param \Magento\Framework\Search\Request\Builder $requestBuilder
     * @param \Magento\Search\Model\SearchEngine $searchEngine
     * @param \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory
     * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
     * @param string $searchRequestName
     * @param SearchResultFactory $searchResultFactory
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     */
    public function __construct(
        \Magento\Framework\Data\Collection\EntityFactory $entityFactory,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Eav\Model\Config $eavConfig,
        \Magento\Framework\App\ResourceConnection $resource,
        \Magento\Eav\Model\EntityFactory $eavEntityFactory,
        \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper,
        \Magento\Framework\Validator\UniversalFactory $universalFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\Module\Manager $moduleManager,
        \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory,
        \Magento\Catalog\Model\ResourceModel\Url $catalogUrl,
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Framework\Stdlib\DateTime $dateTime,
        \Magento\Customer\Api\GroupManagementInterface $groupManagement,
        \Magento\Search\Model\QueryFactory $catalogSearchData,
        \Magento\Framework\Search\Request\Builder $requestBuilder,
        \Magento\Search\Model\SearchEngine $searchEngine,
        \Magento\Framework\Search\Adapter\Mysql\TemporaryStorageFactory $temporaryStorageFactory,
        \Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
        $searchRequestName = 'catalog_view_container',
        SearchResultFactory $searchResultFactory = null
    ) {
        $this->queryFactory = $catalogSearchData;
        if ($searchResultFactory === null) {
            $this->searchResultFactory = \Magento\Framework\App\ObjectManager::getInstance()
                ->get('Magento\Framework\Api\Search\SearchResultFactory');
        }
        parent::__construct(
            $entityFactory,
            $logger,
            $fetchStrategy,
            $eventManager,
            $eavConfig,
            $resource,
            $eavEntityFactory,
            $resourceHelper,
            $universalFactory,
            $storeManager,
            $moduleManager,
            $catalogProductFlatState,
            $scopeConfig,
            $productOptionFactory,
            $catalogUrl,
            $localeDate,
            $customerSession,
            $dateTime,
            $groupManagement,
            $catalogSearchData,
            $requestBuilder,
            $searchEngine,
            $temporaryStorageFactory,
            $connection,
            $searchRequestName,
            $searchResultFactory
        );
        $this->requestBuilder = $requestBuilder;
        $this->searchEngine = $searchEngine;
        $this->temporaryStorageFactory = $temporaryStorageFactory;
        $this->searchRequestName = $searchRequestName;
    }

    /**
     * @deprecated
     * @return \Magento\Search\Api\SearchInterface
     */
    private function getSearch()
    {
        if ($this->search === null) {
            $this->search = ObjectManager::getInstance()->get('\Magento\Search\Api\SearchInterface');
        }
        return $this->search;
    }

    /**
     * @deprecated
     * @return \Magento\Framework\Api\Search\SearchCriteriaBuilder
     */
    private function getSearchCriteriaBuilder()
    {
        if ($this->searchCriteriaBuilder === null) {
            $this->searchCriteriaBuilder = ObjectManager::getInstance()
                ->get('\Magento\Framework\Api\Search\SearchCriteriaBuilder');
        }
        return $this->searchCriteriaBuilder;
    }

    /**
     * @deprecated
     * @return \Magento\Framework\Api\FilterBuilder
     */
    private function getFilterBuilder()
    {
        if ($this->filterBuilder === null) {
            $this->filterBuilder = ObjectManager::getInstance()->get('\Magento\Framework\Api\FilterBuilder');
        }
        return $this->filterBuilder;
    }

    /**
     * @inheritdoc
     */
    protected function _renderFiltersBefore()
    {
        $this->getSearchCriteriaBuilder();
        $this->getFilterBuilder();
        $this->getSearch();

        if ($this->queryText) {
            $this->filterBuilder->setField('search_term');
            $this->filterBuilder->setValue($this->queryText);
            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
        }

        $priceRangeCalculation = $this->_scopeConfig->getValue(
            \Magento\Catalog\Model\Layer\Filter\Dynamic\AlgorithmFactory::XML_PATH_RANGE_CALCULATION,
            \Magento\Store\Model\ScopeInterface::SCOPE_STORE
        );
        if ($priceRangeCalculation) {
            $this->filterBuilder->setField('price_dynamic_algorithm');
            $this->filterBuilder->setValue($priceRangeCalculation);
            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());
        }

        //------------------- Custom Filter ----------------[START]     

            $skus = array('24-MB04', '24-MB03', '24-MB02');

            $this->filterBuilder->setField('sku');
            $this->filterBuilder->setValue($skus);
            $this->filterBuilder->setConditionType('in');

            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());

        //------------------- Custom Filter ----------------[END]

        $searchCriteria = $this->searchCriteriaBuilder->create();
        $searchCriteria->setRequestName($this->searchRequestName);
        try {
            $this->searchResult = $this->getSearch()->search($searchCriteria);
        } catch (EmptyRequestDataException $e) {
            /** @var \Magento\Framework\Api\Search\SearchResultInterface $searchResult */
            $this->searchResult = $this->searchResultFactory->create()->setItems([]);
        } catch (NonExistingRequestNameException $e) {
            $this->_logger->error($e->getMessage());
            throw new LocalizedException(__('Sorry, something went wrong. You can find out more in the error log.'));
        }

        $temporaryStorage = $this->temporaryStorageFactory->create();
        $table = $temporaryStorage->storeApiDocuments($this->searchResult->getItems());

        $this->getSelect()->joinInner(
            [
                'search_result' => $table->getName(),
            ],
            'e.entity_id = search_result.' . TemporaryStorage::FIELD_ENTITY_ID,
            []
        );

        $this->_totalRecords = $this->searchResult->getTotalCount();

        if ($this->order && 'relevance' === $this->order['field']) {
            $this->getSelect()->order('search_result.'. TemporaryStorage::FIELD_SCORE . ' ' . $this->order['dir']);
        }
        //return parent::_renderFiltersBefore();
        return \Magento\Catalog\Model\ResourceModel\Product\Collection::_renderFiltersBefore();
    }

    /**
     * Return field faceted data from faceted search result
     *
     * @param string $field
     * @return array
     * @throws StateException
     */
    public function getFacetedData($field)
    {
        $this->_renderFilters();
        $result = [];
        $aggregations = $this->searchResult->getAggregations();
        // This behavior is for case with empty object when we got EmptyRequestDataException
        if (null !== $aggregations) {
            $bucket = $aggregations->getBucket($field . RequestGenerator::BUCKET_SUFFIX);
            if ($bucket) {
                foreach ($bucket->getValues() as $value) {
                    $metrics = $value->getMetrics();
                    $result[$metrics['value']] = $metrics;
                }
            } else {
                throw new StateException(__('Bucket does not exist'));
            }
        }
        return $result;
    }

}

Note: Replase $skus array with your SKU values, $skus = array('24-MB04', '24-MB03', '24-MB02');

13
  • Nalin, Thanks for providing nice solution. It works fine with sku but it creates problem with custom attribute. I have added custom multiselect attribute called allowed_customer and it stores values in database like 1,2,3 so I want to filter with and match with current logged in customer. I tried using condition type like, finset but i didn't get proper results. If you have any idea from your end, I would be more thankful to you. I have also posted question here link Commented Jun 22, 2017 at 18:18
  • @Nalin Savaliya :- i my case little different, i have my custom table where i need to join this with search table which has two column produtc_id(FK) and customsku, when customer search any key words and match with this custom sku data search result should contain this product also product also it something like custom attribute from custom table , how to do it ? Commented Dec 8, 2017 at 10:43
  • Can possible to overwrite the only function. because _renderFiltersBefore has three private methods. So I can not overwrite single function using this. Commented Apr 10, 2018 at 10:08
  • While class override you should override private members in new class with same code or your desired changes, but it's necessary to have private members in sub class Commented Apr 10, 2018 at 10:12
  • 1
    @NalinSavaliya, This answer helps me a lot, Thank you. upvote! Commented Mar 28, 2019 at 13:26
3

All credits to Mr. Rakesh Jesadiya for this answer.

As per question asked, I think following my answer can help you.

-> There is a Build(...) in Magento\CatalogSearch\Model\Search\IndexBuilder.php . That is actually used to build all layered navigation filters.

What I did is, made plugin to override that Build(...) function and create around method to it.

Added custom collection query as required and returned product ids from the query.

Further added where query in main select query for returning filters.

Create di.xml in your custom module.

Custom/Layernavigation/etc/di.xml

Add following code :

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <!-- Magento Catalog search Override IndexBuilder.php plugin file -->
    <type name="Magento\CatalogSearch\Model\Search\IndexBuilder">
        <plugin name="Custom_Layernavigation::CustomLayernavigation"
                type="Custom\Layernavigation\Plugin\CatalogSearch\Model\Search\IndexBuilder" />
    </type>
</config>

Now, create plugin file to override build function that builds all filters.

Custom/Layernavigation/Plugin/CatalogSearch/Model/Search/IndexBuilder.php

<?php

namespace Custom\Layernavigation\Plugin\CatalogSearch\Model\Search;

use Magento\Framework\DB\Select;
use Magento\Framework\Search\Request\FilterInterface;
use Magento\Framework\Search\Request\Filter\BoolExpression;
use Magento\Framework\Search\Request\Query\Filter;
use Magento\Framework\Search\RequestInterface;
use Magento\Framework\Search\Request\QueryInterface as RequestQueryInterface;
use Magento\Framework\App\ResourceConnection;

class IndexBuilder
{
    /**
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     * @var \Magento\Store\Model\StoreManagerInterface
     */
    protected $storeManager;


    public function __construct(
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
        \Magento\Catalog\Model\Product\Visibility $productVisibility,
        \Magento\Catalog\Helper\Category $categoryHelper,
        \Magento\Framework\Registry $registry
    ) {
        $this->storeManager = $storeManager;
        $this->_productCollectionFactory = $productCollectionFactory; 
        $this->_productVisibility = $productVisibility;
        $this->categoryHelper = $categoryHelper;
        $this->registry = $registry;
    }

    /**
     * Build index query
     *
     * @param $subject
     * @param callable $proceed
     * @param RequestInterface $request
     * @return Select
     * @SuppressWarnings(PHPMD.UnusedFormatParameter)
     */
    public function aroundBuild($subject, callable $proceed, RequestInterface $request)
    {
        $select = $proceed($request);
        $storeId = $this->storeManager->getStore()->getStoreId();
        $rootCatId = $this->storeManager->getStore($storeId)->getRootCategoryId();
        $productUniqueIds = $this->getCustomCollectionQuery();
        $select->where('search_index.entity_id IN (' . join(',', $productUniqueIds) . ')');

        return $select;
    }

    /**
     *
     * @return ProductIds[]
     */
    public function getCustomCollectionQuery() {
        /* get all category ids of current store */
        $websiteId = $this->storeManager->getStore()->getWebsiteId();
        $currentStoreAllCategories = $this->categoryHelper->getStoreCategories(false,true,true);

        $collection = $this->_productCollectionFactory->create();
        $collection->addAttributeToSelect(array('entity_id','sku'));
        // filter current website products
        $collection->addWebsiteFilter($websiteId);
        // set visibility filter
        $collection->setVisibility($this->_productVisibility->getVisibleInSiteIds());

        /*SKU Filter Here*/
        $sku = array('24-MB04', '24-MB03', '24-MB02');
        $collection->addAttributeToFilter('sku', array('in' => $sku));

        $getProductAllIds = $collection->getAllIds();
        $getProductUniqueIds = array_unique($getProductAllIds);
        return $getProductUniqueIds;
    }

}

Please correct me If I'm wrong.

9
  • 2
    Thank you, this solution works perfect for me, Custom product collection and Layered Navigation filters are working as expected. Commented Nov 26, 2018 at 7:51
  • @RonakParmar not working for me in magento 2.2 and no error Commented Dec 18, 2018 at 5:45
  • This build method is not available in magento 2.4.3 so do you have any idea that how we can achieve this? @Rakesh Jesadiya
    – ZealousWeb
    Commented Oct 6, 2021 at 7:28
  • I got answer for magento 2.4.3 as well.
    – ZealousWeb
    Commented Oct 7, 2021 at 14:50
  • Hi ZealousWeb can you share your answer
    – Priyank
    Commented Dec 27, 2021 at 7:52
1

Use Magento's dependency injection. Init Catalog_product model then getCollection and apply attributes to filters functions.

 protected $_catalogLayer;


 public function __construct(

    \Magento\Catalog\Model\Layer\Resolver $layerResolver

    ) {

          $this->_catalogLayer = $layerResolver->get();
    }

 public function _getFilteredCollection(){

           $sku = array('24-MB04', '24-MB03', '24-MB02');              

           $collection =  $this->_catalogLayer->getProductCollection()
                         ->addAttributeToFilter('sku', array('in' => $sku));

           $collection->getSize();

           return $collection;

 }

It';s important to do getSize() on the collection for you to be able to use the toolbar correctly. This will set the size of this collection for the toolbar

         $block \Magento\Catalog\Block\Product\ProductList\Toolbar 
         toolbar.phtml             
         <?php if ($block->getCollection()->getSize()): ?>
3
  • It's working for Toolbar and also updates layered navigation but in layered navigation it displays wrong data. Commented Nov 7, 2016 at 9:57
  • If you could be a little more specific. What is showing? different attributes? cause you can add more filters Commented Nov 7, 2016 at 13:07
  • 1
    Many thanks for your reply. I have updated question with my codes. It filters products based on given SKUs, also pagination and toolbar is working fine. When it comes to layered navigation, it updates count of filters as per collection but, gives wrong data. Suppose in Layered navigation there is Style attribute, after filter it displays two options of Style 1] Luggage (1) 2] Duffel (2) . Now, when I click on Luggage in layered navigation then it show No products, It's because the layered was not updated based on filtered collection. Also Price filter won't get updated Commented Nov 8, 2016 at 5:30
1

Update/override category page collection by filtering please Visit Modify product collection in M2

Above link's example gives collection exactly what you need. But it won't update pagination block and layered navigation in category page, so I achieve this thing in Magento 2.4.1-p1 with below code.

Create plugin in your custom module.

Create di.xml in the module app/code/Vendor/Module/etc/di.xml there is this:

<?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\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper">
        <plugin sortOrder="1" name="Fiter_layer_navigation_Products" type="Vender\Module\Plugin\SearchAdapter\Mapper"/>
    </type>
</config>

and create afterBuildQuery function in plugin app/code/Vendor/Module/Plugin/SearchAdapter/Mapper.php looks like this:

<?php
namespace Vendor\Module\Plugin\SearchAdapter;

/**
 * Mapper class
 */
class Mapper
{
    /**
     * @var \Magento\Framework\App\Request\Http
     */
    protected $request;

    /**
     * @param \Magento\Framework\App\Request\Http $request
     */
    public function __construct(
        \Magento\Framework\App\Request\Http $request
    ) {
        $this->request = $request;
    }

    /**
     * Build adapter dependent query
     *
     * @param \Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper $subject
     * @param array $searchQuery
     * @return array $searchQuery
     */
    public function afterBuildQuery(\Magento\Elasticsearch\Elasticsearch5\SearchAdapter\Mapper $subject, $searchQuery)
    {
        $fullActionName = $this->request->getFullActionName();
        if ($fullActionName == 'catalog_category_view') {
            $productIds = [1,2,3,4,5]; // Get your product ids hare
            if (count($productIds)) {
                $searchQuery['body']['query']['bool']['filter'] = ['ids' => ['values' => $productIds]];
            }
        }
    
        return $searchQuery;
    }
}

Thank You!

3
  • I have applied the collection filter based on a customer attribute. Do I need to mention that above? Facing exactly same issue with pagination and sidebar Commented Mar 4, 2022 at 11:02
  • @Mukesh Khatri i don't understand why hardcode $productIds = [1,2,3,4,5], how to update the pager after the collection is modified by observer actually?
    – hkguile
    Commented May 31, 2023 at 8:17
  • the main point is how to get the loaded product collection in this afterBuildQuery?
    – hkguile
    Commented May 31, 2023 at 9:52
0

[Magento 2.4.6]

Please try the below code.

app/code/VendorName/ModuleName

registration.php

<?php
/**
 * @category Msquare
 * @package Msquare_CustomCollection
 * @author Mahesh Makwana <[email protected]>
 */
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(ComponentRegistrar::MODULE, 'VendorName_ModuleName', __DIR__);

app/code/VendorName/ModuleName/etc

module.xml

<?xml version="1.0" ?>
<!--
/**
 * @category Msquare
 * @package Msquare_CustomCollection
 * @author Mahesh Makwana <[email protected]>
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="VendorName_ModuleName">
        <sequence>
            <module name="Magento_Catalog"/>
            <module name="Magento_Search"/>
            <module name="Magento_CatalogSearch"/>
            <module name="Magento_Elasticsearch"/>
        </sequence>
    </module>
</config>

app/code/VendorName/ModuleName/etc

di.xml

<?xml version="1.0"?>
<!--
/**
 * @category Msquare
 * @package Msquare_CustomCollection
 * @author Mahesh Makwana <[email protected]>
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <virtualType name="VendorName\ModuleName\Model\ResourceModel\Fulltext\CollectionFactory" type="Magento\Catalog\Model\ResourceModel\Product\CollectionFactory">
        <arguments>
            <argument name="instanceName" xsi:type="string">VendorName\ModuleName\Model\ResourceModel\Fulltext\Collection</argument>
        </arguments>
    </virtualType>

    <type name="Magento\CatalogSearch\Model\Layer\Category\ItemCollectionProvider">
        <arguments>
            <argument name="collectionFactory" xsi:type="object">VendorName\ModuleName\Model\ResourceModel\Fulltext\CollectionFactory</argument>
        </arguments>
    </type>

    <virtualType name="Magento\CatalogSearch\Model\ResourceModel\Fulltext\SearchCollection" type="VendorName\ModuleName\Model\ResourceModel\Fulltext\Collection">
        <arguments>
            <argument name="searchRequestName" xsi:type="string">quick_search_container</argument>
        </arguments>
    </virtualType>

    <virtualType name="elasticsearchFulltextSearchCollection" type="VendorName\ModuleName\Model\ResourceModel\Fulltext\Collection">
        <arguments>
            <argument name="searchRequestName" xsi:type="string">quick_search_container</argument>
            <argument name="searchCriteriaResolverFactory" xsi:type="object">elasticsearchSearchCriteriaResolverFactory</argument>
            <argument name="searchResultApplierFactory" xsi:type="object">elasticsearchSearchResultApplier\Factory</argument>
            <argument name="totalRecordsResolverFactory" xsi:type="object">elasticsearchTotalRecordsResolver\Factory</argument>
            <argument name="defaultFilterStrategyApplyChecker" xsi:type="object">Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\DefaultFilterStrategyApplyChecker</argument>
        </arguments>
    </virtualType>

    <virtualType name="elasticsearchCategoryCollection" type="VendorName\ModuleName\Model\ResourceModel\Fulltext\Collection">
        <arguments>
            <argument name="searchRequestName" xsi:type="string">catalog_view_container</argument>
            <argument name="searchCriteriaResolverFactory" xsi:type="object">elasticsearchSearchCriteriaResolverFactory</argument>
            <argument name="searchResultApplierFactory" xsi:type="object">elasticsearchSearchResultApplier\Factory</argument>
            <argument name="totalRecordsResolverFactory" xsi:type="object">elasticsearchTotalRecordsResolver\Factory</argument>
            <argument name="defaultFilterStrategyApplyChecker" xsi:type="object">Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\DefaultFilterStrategyApplyChecker</argument>
        </arguments>
    </virtualType>

    <virtualType name="elasticsearchLayerCategoryItemCollectionProvider" type="Magento\Elasticsearch\Model\Layer\Category\ItemCollectionProvider">
        <arguments>
            <argument name="factories" xsi:type="array">
                <item name="mysql" xsi:type="object">VendorName\ModuleName\Model\ResourceModel\Fulltext\CollectionFactory</item>
            </argument>
        </arguments>
    </virtualType>

</config>

VendorName/ModuleName/etc

search_request.xml

<?xml version="1.0"?>
<!--
/**
 * @category Msquare
 * @package Msquare_CustomCollection
 * @author Mahesh Makwana <[email protected]>
 */
-->
<requests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Search/etc/search_request.xsd">

    <request query="catalog_view_container" index="catalogsearch_fulltext">
        <queries>
            <query xsi:type="boolQuery" name="catalog_view_container" boost="1">
                <queryReference clause="should" ref="sku_query"/>
            </query>
            <query name="sku_query" xsi:type="filteredQuery">
                <filterReference clause="must" ref="sku_query_filter"/>
            </query>
        </queries>
        <filters>
            <filter xsi:type="termFilter" name="sku_query_filter" field="sku" value="$sku$"/>
        </filters>
        <from>0</from>
        <size>10000</size>
    </request>
</requests>

VendorName/ModuleName/Model/ResourceModel/Fulltext

Collection.php

<?php
/**
 * @category Msquare
 * @package Msquare_CustomCollection
 * @author Mahesh Makwana <[email protected]>
 */
namespace VendorName\ModuleName\Model\ResourceModel\Fulltext;

use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\DefaultFilterStrategyApplyChecker;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\DefaultFilterStrategyApplyCheckerInterface;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\TotalRecordsResolverInterface;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\TotalRecordsResolverFactory;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolverInterface;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolverFactory;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierFactory;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplierInterface;
use Magento\Framework\Data\Collection\Db\SizeResolverInterfaceFactory;
use Magento\Framework\DB\Select;
use Magento\Framework\Api\Search\SearchResultInterface;
use Magento\CatalogSearch\Model\Search\RequestGenerator;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Exception\StateException;
use Magento\Framework\Search\Request\EmptyRequestDataException;
use Magento\Framework\Search\Request\NonExistingRequestNameException;
use Magento\Framework\Api\Search\SearchResultFactory;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\App\ObjectManager;
use Magento\Catalog\Model\ResourceModel\Product\Collection\ProductLimitationFactory;
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection as ParentCollection;

/**
 * Fulltext Collection
 *
 * This collection should be refactored to not have dependencies on MySQL-specific implementation.
 *
 * @api
 * @since 100.0.2
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 * @SuppressWarnings(PHPMD.TooManyFields)
 * @SuppressWarnings(PHPMD.CookieAndSessionMisuse)
 */
class Collection extends ParentCollection
{
    /**
     * @var string
     */
    private $queryText;

    /**
     * @var string
     */
    private $searchRequestName;

    /**
     * @var \Magento\Search\Api\SearchInterface
     */
    private $search;

    /**
     * @var \Magento\Framework\Api\Search\SearchCriteriaBuilder
     */
    private $searchCriteriaBuilder;

    /**
     * @var \Magento\Framework\Api\Search\SearchResultInterface
     */
    private $searchResult;

    /**
     * @var SearchResultFactory
     */
    private $searchResultFactory;

    /**
     * @var \Magento\Framework\Api\FilterBuilder
     */
    private $filterBuilder;

    /**
     * @var SearchCriteriaResolverFactory
     */
    private $searchCriteriaResolverFactory;

    /**
     * @var SearchResultApplierFactory
     */
    private $searchResultApplierFactory;

    /**
     * @var TotalRecordsResolverFactory
     */
    private $totalRecordsResolverFactory;

    /**
     * @var array
     */
    private $searchOrders;

    /**
     * @var DefaultFilterStrategyApplyCheckerInterface
     */
    private $defaultFilterStrategyApplyChecker;

    /**
     * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
     * @param \Psr\Log\LoggerInterface $logger
     * @param \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy
     * @param \Magento\Framework\Event\ManagerInterface $eventManager
     * @param \Magento\Eav\Model\Config $eavConfig
     * @param \Magento\Framework\App\ResourceConnection $resource
     * @param \Magento\Eav\Model\EntityFactory $eavEntityFactory
     * @param \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper
     * @param \Magento\Framework\Validator\UniversalFactory $universalFactory
     * @param \Magento\Store\Model\StoreManagerInterface $storeManager
     * @param \Magento\Framework\Module\Manager $moduleManager
     * @param \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory
     * @param \Magento\Catalog\Model\ResourceModel\Url $catalogUrl
     * @param \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate
     * @param \Magento\Customer\Model\Session $customerSession
     * @param \Magento\Framework\Stdlib\DateTime $dateTime
     * @param \Magento\Customer\Api\GroupManagementInterface $groupManagement
     * @param mixed $catalogSearchData
     * @param mixed $requestBuilder
     * @param mixed $searchEngine
     * @param mixed $temporaryStorageFactory
     * @param \Magento\Framework\DB\Adapter\AdapterInterface|null $connection
     * @param string $searchRequestName
     * @param SearchResultFactory|null $searchResultFactory
     * @param ProductLimitationFactory|null $productLimitationFactory
     * @param MetadataPool|null $metadataPool
     * @param \Magento\Search\Api\SearchInterface|null $search
     * @param \Magento\Framework\Api\Search\SearchCriteriaBuilder|null $searchCriteriaBuilder
     * @param \Magento\Framework\Api\FilterBuilder|null $filterBuilder
     * @param SearchCriteriaResolverFactory|null $searchCriteriaResolverFactory
     * @param SearchResultApplierFactory|null $searchResultApplierFactory
     * @param TotalRecordsResolverFactory|null $totalRecordsResolverFactory
     * @param DefaultFilterStrategyApplyCheckerInterface|null $defaultFilterStrategyApplyChecker
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function __construct(
        \Magento\Framework\Data\Collection\EntityFactory $entityFactory,
        \Psr\Log\LoggerInterface $logger,
        \Magento\Framework\Data\Collection\Db\FetchStrategyInterface $fetchStrategy,
        \Magento\Framework\Event\ManagerInterface $eventManager,
        \Magento\Eav\Model\Config $eavConfig,
        \Magento\Framework\App\ResourceConnection $resource,
        \Magento\Eav\Model\EntityFactory $eavEntityFactory,
        \Magento\Catalog\Model\ResourceModel\Helper $resourceHelper,
        \Magento\Framework\Validator\UniversalFactory $universalFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Framework\Module\Manager $moduleManager,
        \Magento\Catalog\Model\Indexer\Product\Flat\State $catalogProductFlatState,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Catalog\Model\Product\OptionFactory $productOptionFactory,
        \Magento\Catalog\Model\ResourceModel\Url $catalogUrl,
        \Magento\Framework\Stdlib\DateTime\TimezoneInterface $localeDate,
        \Magento\Customer\Model\Session $customerSession,
        \Magento\Framework\Stdlib\DateTime $dateTime,
        \Magento\Customer\Api\GroupManagementInterface $groupManagement,
        $catalogSearchData = null,
        $requestBuilder = null,
        $searchEngine = null,
        $temporaryStorageFactory = null,
        \Magento\Framework\DB\Adapter\AdapterInterface $connection = null,
        $searchRequestName = 'catalog_view_container',
        SearchResultFactory $searchResultFactory = null,
        ProductLimitationFactory $productLimitationFactory = null,
        MetadataPool $metadataPool = null,
        \Magento\Search\Api\SearchInterface $search = null,
        \Magento\Framework\Api\Search\SearchCriteriaBuilder $searchCriteriaBuilder = null,
        \Magento\Framework\Api\FilterBuilder $filterBuilder = null,
        SearchCriteriaResolverFactory $searchCriteriaResolverFactory = null,
        SearchResultApplierFactory $searchResultApplierFactory = null,
        TotalRecordsResolverFactory $totalRecordsResolverFactory = null,
        DefaultFilterStrategyApplyCheckerInterface $defaultFilterStrategyApplyChecker = null
    ) {
        $this->searchResultFactory = $searchResultFactory
            ?? ObjectManager::getInstance()->get(SearchResultFactory::class);
        parent::__construct(
            $entityFactory,
            $logger,
            $fetchStrategy,
            $eventManager,
            $eavConfig,
            $resource,
            $eavEntityFactory,
            $resourceHelper,
            $universalFactory,
            $storeManager,
            $moduleManager,
            $catalogProductFlatState,
            $scopeConfig,
            $productOptionFactory,
            $catalogUrl,
            $localeDate,
            $customerSession,
            $dateTime,
            $groupManagement,
            $catalogSearchData,
            $requestBuilder,
            $searchEngine,
            $temporaryStorageFactory,
            $connection,
            $searchRequestName,
            $searchResultFactory,
            $productLimitationFactory,
            $metadataPool,
            $search,
            $searchCriteriaBuilder,
            $filterBuilder,
            $searchCriteriaResolverFactory,
            $searchResultApplierFactory,
            $totalRecordsResolverFactory,
            $defaultFilterStrategyApplyChecker
        );
        $this->searchRequestName = $searchRequestName;
        $this->search = $search ?: ObjectManager::getInstance()->get(\Magento\Search\Api\SearchInterface::class);
        $this->searchCriteriaBuilder = $searchCriteriaBuilder ?: ObjectManager::getInstance()
            ->get(\Magento\Framework\Api\Search\SearchCriteriaBuilder::class);
        $this->filterBuilder = $filterBuilder ?: ObjectManager::getInstance()
            ->get(\Magento\Framework\Api\FilterBuilder::class);
        $this->searchCriteriaResolverFactory = $searchCriteriaResolverFactory ?: ObjectManager::getInstance()
            ->get(SearchCriteriaResolverFactory::class);
        $this->searchResultApplierFactory = $searchResultApplierFactory ?: ObjectManager::getInstance()
            ->get(SearchResultApplierFactory::class);
        $this->totalRecordsResolverFactory = $totalRecordsResolverFactory ?: ObjectManager::getInstance()
            ->get(TotalRecordsResolverFactory::class);
        $this->defaultFilterStrategyApplyChecker = $defaultFilterStrategyApplyChecker ?: ObjectManager::getInstance()
            ->get(DefaultFilterStrategyApplyChecker::class);
    }

    ........................................................................................................................
    ........................................................................................................................
    ........................................................................................................................

    /**
     * @inheritdoc
     */
    protected function _renderFiltersBefore()
    {
        if ($this->isLoaded()) {
            return;
        }

        if ($this->searchRequestName !== 'quick_search_container'
            || ($this->queryText && strlen(trim($this->queryText)))
        ) {
            $this->prepareSearchTermFilter();
            $this->preparePriceAggregation();

            /* Custom Code for SKU start */

            $skus = array('24-MB04', '24-MB03', '24-MB02'); // add your SKU here

            $this->filterBuilder->setField('sku');
            $this->filterBuilder->setValue($skus);
            $this->filterBuilder->setConditionType('in');

            $this->searchCriteriaBuilder->addFilter($this->filterBuilder->create());

            /* Custom Code for SKU over */

            $searchCriteria = $this->getSearchCriteriaResolver()->resolve();
            try {
                $this->searchResult =  $this->getSearch()->search($searchCriteria);
                $this->_totalRecords = $this->getTotalRecordsResolver($this->searchResult)->resolve();
            } catch (EmptyRequestDataException $e) {
                $this->searchResult = $this->createEmptyResult();
            } catch (NonExistingRequestNameException $e) {
                $this->_logger->error($e->getMessage());
                throw new LocalizedException(__('An error occurred. For details, see the error log.'));
            }
        } else {
            $this->searchResult = $this->createEmptyResult();
        }

        $this->getSearchResultApplier($this->searchResult)->apply();
        parent::_renderFiltersBefore();
    }

    ........................................................................................................................
    ........................................................................................................................
    ........................................................................................................................

}

Click here to download whole source file. Collection.php

Click here to download Module

Your Answer

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

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