I'm trying to find the portion of code where the filters from the layered navigation are applied to the product collection.

The method called for product collection retrival is Magento\Catalog\Block\Product\ListProduct::_getProductCollection

Which in turn calls Magento\Catalog\Model\Layer::getProductCollection this uses a collection provider to instantiate the collection and then Magento\Catalog\Model\Layer::prepareProductCollection().

The layer object above contains the filters selected in the layered navigation on frontend and above metioned function (prepare) uses Magento\Catalog\Model\Layer\Category\CollectionFilter::filter.

As stated in the function associated comment this should filter the collection * Filter product collection yet I see no filters from the layered navigation applied.

I still can not find where the filters from the layered navigation are applied.

I should mention that the Magento\Catalog\Block\Product\ListProduct::_getProductCollection is called in the Magento\Catalog\Block\Product\ListProduct::_beforeToHtml method at then end of this method the filtered collection is loaded but even going with the debugger line by line I still can not find the portion of code that applies the filters.

In this method an 'catalog_block_product_list_collection' event is dispatched. Is it possible that the filters are applied in an Observer?


The only observer tapped to this event is Magento\Review\Observer\CatalogBlockProductCollectionBeforeToHtmlObserver and it does not seem to be related to my problem.

Edit 2:

The product collection used in Magento\Catalog\Block\Product\LitProduct is extended from Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection which has a method _renderFiltersBefore which is called when the collection is loaded. Here the filters are fed and set through a searchCriteriaBuilder the search is made and put in a temporary table which is joined with the productCollection.

I still can not find the actual piece of code that retrieves and handles the filters.

Any help is appreciated.

Many thanks.

2 Answers 2


I found the answer for this. (I think) While it makes sense at some level. The flow is kinda messed up. Here it goes:

As I stated in my question the collection used to display the products is a descendant of Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection that uses a SearchCriteriaBuilder to perform the actual search.

Still when debugging the actual display of the products I could not find any place where the filters were added. But in the _renderFiltersBefore() (method that is called before loading the collection) when the searchCriteriaBuilder was retrieved the filters were already there.

The searchCriteriaBuilder is retrieved using the objectManager::get() which implements a singleton pattern.

Long story short. The same collection(object type not instance) is used for the navigation block. And when rendering it the filters are applied here Magento\LayeredNavigation\Block\Navigation::_prepareLayout()

This are two separate blocks, working with different instances. But because of the singleton approach in retrieving the searchCriteriaBuilder when the ListProduct block loads the collection the filters are already set.

Basically what they've done (from what I can tell) is coupled the shit out of this two blocks.

  1. The layered navigation block is rendered and here the filters are applied.
  2. When rendering the Product List they "know" that the filters were already applied and go ahead with loading the collection.

I find this to be messed up on so many levels. If someone can prove me wrong. Please do.


Filters are added to searchCriteriaBuilder by the addFieldToFilter() function.

For example in Magento\CatalogSearch\Model\Layer\Filter\Attribute:

public function apply(\Magento\Framework\App\RequestInterface $request)
    $attributeValue = $request->getParam($this->_requestVar);
    if (empty($attributeValue) && !is_numeric($attributeValue)) {
        return $this;

    $attribute = $this->getAttributeModel();
    /** @var \Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection $productCollection */
    $productCollection = $this->getLayer()
    $productCollection->addFieldToFilter($attribute->getAttributeCode(), $attributeValue);

    $labels = [];
    foreach ((array) $attributeValue as $value) {
        $label = $this->getOptionText($value);
        $labels[] = is_array($label) ? $label : [$label];
    $label = implode(',', array_unique(array_merge(...$labels)));
        ->addFilter($this->_createItem($label, $attributeValue));

    //$this->setItems([]); // set items to disable show filtering
    return $this;

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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