سؤال

How can i filter the Magento2 Fulltext\Collection (Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection) by an OR condition between 2 product attributes, with respecting also the searchcriteria results ?

What I tried so far:

  1. Just to use addAttributeToFilter from the ProductCollection, because acutally the CatalogSearch Fulltext Collection extends the ProductCollection. This works fine on the first look, but while other filters in the Fulltext Collection are applied using addFieldToFilter instead of addAttributeToFilter, using addAttributeToFilter is not adding the filters to the searchCriteriaBuilder of the Fulltext Collection.
    This results in that core functionality which uses the searchCriteria results directly, instead of the full result of the ProductCollection won't work with the applied filters.

  2. Use addFieldToFilter with multi fields, because you can add or conditions with the addFieldToFilter from the AbstractCollection, no ?
    no! - because addFieldToFilter is overwritten in the Fulltext Collection, and this overwritten method does only support single fields.

  3. Use SearchCriteriaBuilder with DI in an own class and add the OR condition like described here: Magento2: How to add multiple AND and OR conditions to filter/filterGroups? .
    This again also does'nt work because the Fulltext Collection uses the \Magento\Framework\Api\Search\SearchCriteriaBuilder instead of \Magento\Framework\Api\SearchCriteriaBuilder, which does not support this functionality.

Some code snippet to the way i implemented it for (1.) to help you understand what i want to achieve:

$fulltextCollection->addAttributeToFilter(
  [
    [
      'attribute' => 'custom_attribute',
      'eq' => 2
    ],
    [
      'attribute' => 'another_custom_attribute',
      'eq' => 1
    ]
  ],
  [],
  Select::JOIN_LEFT
);

Any ideas are welcome!

هل كانت مفيدة؟

المحلول

After digging into the Magento Core code an additional one and a half day i finally found a solution for my issue. If you expect a short answer here, or a simple fix, lets just say, "there is none", at least in the way its currently implemented in the Magento2 Core. A lot of the used classes are deprecated, so lets hope this mess is cleaned up in some future release.

Digging further into the functionality of the Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection i first found out that the problem, the Fulltext Collection's addFieldToFilter method does not support OR-conditions lays not in the Fulltext Collection itself, but in the Magento2 Cores search-engine.
Have a look at the method Magento\Framework\Search\Search::addFieldToFilter which is called during the responsible methods of the Fulltext Collection (i won't show all the callstacks here). This method has nearly the same functionality as the Fulltext Collection addFieldToFilter method and also does not support OR-conditions.

So let's dig deeper into how that search actually works...

  • Starting from Magento\Framework\Search\Search::search a search request is built using the SearchRequestBuilder adding all the filters and so on from the searchCriteria to the request object. The search is then performed using $this->searchEngine->search($request);

  • Magento\Search\Model\SearchEngine::search just passes the the SearchRequest object to the SearchAdapter creating the adapter using the AdapterFactory

  • Magento\Framework\Search\Adapter\Mysql\Adapter is the SearchAdapter which is used (and which is deprecated already since 102.0.0). But still here all the magic happens. The search builds up an SQL query using the Magento\Framework\Search\Adapter\Mysql\Mapper and creates a new temporary mysql table with the results of the searchRequest.

To get some steps back, in the Fulltext Collection this temporary table is inner joined to the product collection select, filtering the results from the product collection by the results in the search_tmp table.

If you follow the call stack of Magento\Framework\Search\Adapter\Mysql\Adapter a bit further, at the end you may find the 2 methods i modified to fix the issue i had:


Magento\CatalogSearch\Model\Search\FilterMapper\CustomAttributeFilter::apply called in Magento\CatalogSearch\Model\Search\FilterMapper\FilterMapper.
(also deprecated since 101.0.0)

This method is responsible for joining the catalog_product_index_eav tables for each of your custom attributes. Still without filters, except if only one custom attribute filter is selected, then it's also already handling the filtering by that attribute. While this class by default uses inner joins for my case i needed left joins, to join both new attributes to be able to build a or condition, even if one of both attributes does not have a value for a product.
So i decided to modify this by an after plugin to modify the joins of my custom attributes to left join and also to join an additional table if one of the 2 attributes are applied as filters.


Magento\CatalogSearch\Model\Adapter\Mysql\Filter\Preprocessor::process called in Magento\Framework\Search\Adapter\Mysql\Filter\Builder::processFilter
(also deprecated since 101.0.0)

This method is responsible for building the WHERE part in the sql select. It is called once for each filter applied and returns a string containing a single part of the WHERE. This parts are afterwards joined to build the full WHERE part of the query.

So i decided to modify this as well by using an after plugin extending the base condition by the OR on the other table which is joined in the other plugin.


That's it, i know, it's dirty, but sadly still the cleanest way i found after some days of research.
I will leave this question still open for the next days if someone has a better solution in mind; and will approve my own answer in a few days if not.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى magento.stackexchange
scroll top