I have customized(override) product list collection getProductCollection and filtered collection as required. Added below.
public function getProductCollection() { if (isset($this->_productCollections[$this->getCurrentCategory()->getId()])) { $collection = $this->_productCollections[$this->getCurrentCategory()->getId()]; } else { $collection = $this->collectionProvider->getCollection($this->getCurrentCategory()); $this->prepareProductCollection($collection); $this->_productCollections[$this->getCurrentCategory()->getId()] = $collection; } /* custom filter condition starts here*/ $productUniqueIds = array(10,11,12,13); $collection->addAttributeToFilter('entity_id',array('in' => $productUniqueIds)); /* custom filter condition ends here */ return $collection; }
But I am facing an issue in the layered navigation product count after applying filters on product collection. All counts are wrong. What I guess is I need to apply custom filters on layered navigation's collection as well, but I am not able to find it so far.
I have debugged and reached till _createItem function in vendor/magento/module-catalog/Model/Layer/Filter/AbstractFilter.php file. Product counts are coming from there, but I couldn't find from where exactly product collection gets returned.
Please share your ideas if you have. Any help would be appreciated.
Solved! Go to Solution.
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.
For Magento 2, if you want to give custom conditions for product collection, your custom conditions works fine for your product collection in listing page but when you check for layered navigation in the left sidebar, You may have the wrong result for product collection.
When you set custom conditions for product collection by before or after plugin for getLoadedProductCollection() or override php file, that print proper query for your result and when you check that query with a database you gave the proper result for a product.
In Magento 2 you need to override or create a plugin for build() function of IndexBuilder.php file from Catalog search module to accomplish your task.
Check the blog for details instructions, Set Custom conditions for product collection
You need to create a module, to apply custom conditions to product collection and based on above blogs module you got a proper result for collection.
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.
For Magento 2, if you want to give custom conditions for product collection, your custom conditions works fine for your product collection in listing page but when you check for layered navigation in the left sidebar, You may have the wrong result for product collection.
When you set custom conditions for product collection by before or after plugin for getLoadedProductCollection() or override php file, that print proper query for your result and when you check that query with a database you gave the proper result for a product.
In Magento 2 you need to override or create a plugin for build() function of IndexBuilder.php file from Catalog search module to accomplish your task.
Check the blog for details instructions, Set Custom conditions for product collection
You need to create a module, to apply custom conditions to product collection and based on above blogs module you got a proper result for collection.
@kazim_noorani @Rakesh Jesadiya It's not working after enabling elastic search. Can anyone please help with this?
Is the issue is fixed in elastic search?
Thanks for posting the code in your answer.
Did you find a solution to the Elastic Search problem? I have the same problem.
The mentioned IndexBuilder does not exist in Magento 2.4. Is there any other class available as per Magento 2.4
2 years later any updates on this? We are in desperate need for a solution in Magento 2.4
Seems like this is impossible in Magento 2.4.x because of Elastic search.
Because Elastic search returns filtered product ids and aggregation data (basically collection count and filter counts along with product ids)
This products ids will be again searched in MySQL to get category page collection, So we can modify only the product collection as much as we need in MySQL search but not the count and layer navigation counts.
In my case I added one drop down attribute and went with the Magento filtration flow. At least I can have this attribute values will be indexed to Elastic search and can be filtered.
(I can customise the values that applied to filter dynamically so it helped to achieve what I need)