0

Update I think this is where it is going wrong:

    ItemCollectionProviderInterface $collectionProvider = null,
    ProductCollectionPrepareStrategyProvider $productCollectionPrepareStrategyProvider = null

and

    $this->collectionProvider = $collectionProvider;
    $this->productCollectionPrepareStrategyProvider = $productCollectionPrepareStrategyProvider
        ?: ObjectManager::getInstance()->get(ProductCollectionPrepareStrategyProvider::class);

This is in the original class, so I thought I had to put it into the constructor, too, like this. But I think this is wrong and causing the errors. Someone knows how it should be written here?

Original

I have this error when making an override for \Magento\CatalogSearch\Model\Advanced

main.CRITICAL: Error: Call to undefined method Magento\Catalog\Model\ResourceModel\Product\Collection\Interceptor::addFieldsToFilter() in Vendor/Module/Model/Advanced.php:179

I know that this AddFieldsToFilter function is in Magento\CatalogSearch\Model\ResourceModel\Advanced and that this one extends \Magento\Catalog\Model\ResourceModel\Product\Collection

But why does my override try to call this function in \Magento\Catalog\Model\ResourceModel\Product\Collection and not in Magento\CatalogSearch\Model\ResourceModel\Advanced?

I did this (complete code of this override file):

namespace Vendor\Module\Model;

use Magento\Catalog\Model\Config;
use Magento\CatalogSearch\Model\Advanced\ProductCollectionPrepareStrategyProvider;
use Magento\CatalogSearch\Model\Search\ItemCollectionProviderInterface;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory as ProductCollectionFactory;
use Magento\CatalogSearch\Model\ResourceModel\Advanced\Collection as ProductCollection;
use Magento\CatalogSearch\Model\ResourceModel\AdvancedFactory;
use Magento\Directory\Model\CurrencyFactory;
use Magento\Eav\Model\Entity\Attribute as EntityAttribute;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\App\ObjectManager;

class Advanced extends \Magento\CatalogSearch\Model\Advanced
{

    protected $_searchCriterias = [];
    protected $_productCollection;
    protected $_catalogConfig;
    protected $_catalogProductVisibility;
    protected $_attributeCollectionFactory;
    protected $_storeManager;
    protected $_productFactory;
    protected $_currencyFactory;
    protected $productCollectionFactory;
    private $collectionProvider;
    private $productCollectionPrepareStrategyProvider;

      public function __construct(
        \Magento\Framework\Model\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeCollectionFactory,
        \Magento\Catalog\Model\Product\Visibility $catalogProductVisibility,
        \Magento\Catalog\Model\Config $catalogConfig,
        \Magento\Directory\Model\CurrencyFactory $currencyFactory,
        \Magento\Catalog\Model\ProductFactory $productFactory,
        \Magento\Store\Model\StoreManagerInterface $storeManager,
        \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory,
        \Magento\CatalogSearch\Model\ResourceModel\AdvancedFactory $advancedFactory,
        array $data = [],
        \Magento\CatalogSearch\Model\Search\ItemCollectionProviderInterface $collectionProvider = null,
        \Magento\CatalogSearch\Model\Advanced\ProductCollectionPrepareStrategyProvider $productCollectionPrepareStrategyProvider = null
    ) {
        $this->_attributeCollectionFactory = $attributeCollectionFactory;
        $this->_catalogProductVisibility = $catalogProductVisibility;
        $this->_catalogConfig = $catalogConfig;
        $this->_currencyFactory = $currencyFactory;
        $this->_productFactory = $productFactory;
        $this->_storeManager = $storeManager;
        $this->productCollectionFactory = $productCollectionFactory;
        $this->collectionProvider = $collectionProvider;
        $this->productCollectionPrepareStrategyProvider = $productCollectionPrepareStrategyProvider
            ?: ObjectManager::getInstance()->get(ProductCollectionPrepareStrategyProvider::class);

        parent::__construct(
            $context,
            $registry,
            $attributeCollectionFactory,
            $catalogProductVisibility,
            $catalogConfig,
            $currencyFactory,
            $productFactory,
            $storeManager,
            $productCollectionFactory,
            $advancedFactory,
            $data,
            $collectionProvider = null,
            $productCollectionPrepareStrategyProvider = null
        );


    }

    /**
     * Add advanced search filters to product collection
     *
     * @param array $values
     * @return $this
     * @throws LocalizedException
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    public function addFilters($values)
    {
        $attributes = $this->getAttributes();
        $allConditions = [];

        foreach ($attributes as $attribute) {
            /* @var $attribute Attribute */
            if (!isset($values[$attribute->getAttributeCode()])) {
                continue;
            }
            if ($attribute->getFrontendInput() == 'text' || $attribute->getFrontendInput() == 'textarea') {
                if (!trim($values[$attribute->getAttributeCode()])) {
                    continue;
                }
            }
            $value = $values[$attribute->getAttributeCode()];
            $preparedSearchValue = $this->getPreparedSearchCriteria($attribute, $value);
            if (false === $preparedSearchValue) {
                continue;
            }
            $this->addSearchCriteria($attribute, $preparedSearchValue);

            if ($attribute->getAttributeCode() == 'price') {
                $rate = 1;
                $store = $this->_storeManager->getStore();
                $currency = $store->getCurrentCurrencyCode();
                if ($currency != $store->getBaseCurrencyCode()) {
                    $rate = $store->getBaseCurrency()->getRate($currency);
                }

                $value['from'] = (isset($value['from']) && is_numeric($value['from']))
                    ? (float)$value['from'] / $rate
                    : '';
                $value['to'] = (isset($value['to']) && is_numeric($value['to']))
                    ? (float)$value['to'] / $rate
                    : '';
            }

            if ($attribute->getBackendType() == 'datetime') {
                $value['from'] = (isset($value['from']) && !empty($value['from']))
                    ? date('Y-m-d\TH:i:s\Z', strtotime($value['from']))
                    : '';
                $value['to'] = (isset($value['to']) && !empty($value['to']))
                    ? date('Y-m-d\TH:i:s\Z', strtotime($value['to']))
                    : '';
            }
            $condition = $this->_getResource()->prepareCondition(
                $attribute,
                $value,
                $this->getProductCollection()
            );
            if ($condition === false) {
                continue;
            }

            $table = $attribute->getBackend()->getTable();
            if ($attribute->getBackendType() == 'static') {
                $attributeId = $attribute->getAttributeCode();
            } else {
                $attributeId = $attribute->getId();
            }
            $allConditions[$table][$attributeId] = $condition;
        }
        if ($allConditions) {
            $this->_registry->register('advanced_search_conditions', $allConditions);
            $this->getProductCollection()->addFieldsToFilter($allConditions);
        } else {
            throw new LocalizedException(__('Enter a search term and try again.'));
        }

        return $this;
    }

    /**
     * Retrieve array of attributes used in advanced search
     *
     * @return array|\Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection
     */
    public function getAttributes()
    {
        $attributes = $this->getData('attributes');
        if ($attributes === null) {
            $product = $this->_productFactory->create();
            $attributes = $this->_attributeCollectionFactory
                ->create()
                ->addHasOptionsFilter()
                ->addDisplayInAdvancedSearchFilter()
                ->addStoreLabel($this->_storeManager->getStore()->getId())
                ->setOrder('main_table.attribute_id', 'asc')
                ->load();
            foreach ($attributes as $attribute) {
                $attribute->setEntity($product->getResource());
            }
            $this->setData('attributes', $attributes);
        }
        return $attributes;
    }

    /**
     * Retrieve advanced search product collection
     *
     * @return Collection
     */
    public function getProductCollection()
    {
        if ($this->_productCollection === null) {
            $collection = $this->resolveProductCollection();
            $this->prepareProductCollection($collection);
            if (!$collection) {
                return $collection;
            }
            $this->_productCollection = $collection;
        }

        return $this->_productCollection;
    }

    /**
     * Resolve product collection.
     *
     * @return \Magento\Catalog\Model\ResourceModel\Product\Collection|\Magento\Framework\Data\Collection
     */
    private function resolveProductCollection()
    {
        return (null === $this->collectionProvider)
            ? $this->productCollectionFactory->create()
            : $this->collectionProvider->getCollection();
    }

    /**
     * Prepare product collection
     *
     * @param Collection $collection
     * @return $this
     */
    public function prepareProductCollection($collection)
    {
        $this->productCollectionPrepareStrategyProvider->getStrategy()->prepare($collection);

        return $this;
    }

    /**
     * Add search criteria.
     *
     * @param EntityAttribute $attribute
     * @param mixed $value
     * @return void
     */
    protected function addSearchCriteria($attribute, $value)
    {
        if (!empty($value)) {
            $this->_searchCriterias[] = ['name' => $attribute->getStoreLabel(), 'value' => $value];
        }
    }

    /**
     * Add data about search criteria to object state
     *
     * @todo: Move this code to block
     *
     * @param EntityAttribute $attribute
     * @param mixed $value
     * @return string|bool
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
     * @SuppressWarnings(PHPMD.NPathComplexity)
     */
    protected function getPreparedSearchCriteria($attribute, $value)
    {
        if (is_array($value)) {
            if (isset($value['from']) && isset($value['to'])) {
                if (!empty($value['from']) || !empty($value['to'])) {
                    if (isset($value['currency'])) {
                        /** @var $currencyModel Currency */
                        $currencyModel = $this->_currencyFactory->create()->load($value['currency']);
                        $from = $currencyModel->format($value['from'], [], false);
                        $to = $currencyModel->format($value['to'], [], false);
                    } else {
                        $currencyModel = null;
                    }

                    if (strlen($value['from']) > 0 && strlen($value['to']) > 0) {
                        // -
                        $value = sprintf(
                            '%s - %s',
                            $currencyModel ? $from : $value['from'],
                            $currencyModel ? $to : $value['to']
                        );
                    } elseif (strlen($value['from']) > 0) {
                        // and more
                        $value = __('%1 and greater', $currencyModel ? $from : $value['from']);
                    } elseif (strlen($value['to']) > 0) {
                        // to
                        $value = __('up to %1', $currencyModel ? $to : $value['to']);
                    }
                } else {
                    return '';
                }
            }
        }

        if (($attribute->getFrontendInput() == 'select' ||
                $attribute->getFrontendInput() == 'multiselect') && is_array($value)
        ) {
            foreach ($value as $key => $val) {
                $value[$key] = $attribute->getSource()->getOptionText($val);

                if (is_array($value[$key])) {
                    $value[$key] = $value[$key]['label'];
                }
            }
            $value = implode(', ', $value);
        } elseif ($attribute->getFrontendInput() == 'select' || $attribute->getFrontendInput() == 'multiselect') {
            $value = $attribute->getSource()->getOptionText($value);
            if (is_array($value)) {
                $value = $value['label'];
            }
        } elseif ($attribute->getFrontendInput() == 'boolean') {
            if (is_numeric($value)) {
                $value = $value == 1 ? __('Yes') : __('No');
            } else {
                $value = false;
            }
        }

        return $value;
    }

    /**
     * Returns prepared search criterias in text
     *
     * @return array
     */
    public function getSearchCriterias()
    {
        return $this->_searchCriterias;
    }
}

I guess my real question is how do I write the constructor of my new override class. I really have no clue. This is the original:

class Advanced extends \Magento\Framework\Model\AbstractModel
{
    /**
     * User friendly search criteria list
     *
     * @var array
     */
    protected $_searchCriterias = [];

    /**
     * Product collection
     *
     * @var ProductCollection
     */
    protected $_productCollection;

    /**
     * Initialize dependencies
     *
     * @deprecated 101.0.2
     * @var Config
     */
    protected $_catalogConfig;

    /**
     * Catalog product visibility
     *
     * @var Visibility
     */
    protected $_catalogProductVisibility;

    /**
     * Attribute collection factory
     *
     * @var AttributeCollectionFactory
     */
    protected $_attributeCollectionFactory;

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

    /**
     * Product factory
     *
     * @var ProductFactory
     */
    protected $_productFactory;

    /**
     * Currency factory
     *
     * @var CurrencyFactory
     */
    protected $_currencyFactory;

    /**
     * Advanced Collection Factory
     *
     * @deprecated
     * @see $collectionProvider
     * @var ProductCollectionFactory
     */
    protected $productCollectionFactory;

    /**
     * @var ItemCollectionProviderInterface
     */
    private $collectionProvider;

    /**
     * @var ProductCollectionPrepareStrategyProvider|null
     */
    private $productCollectionPrepareStrategyProvider;

    /**
     * Construct
     *
     * @param Context $context
     * @param Registry $registry
     * @param AttributeCollectionFactory $attributeCollectionFactory
     * @param Visibility $catalogProductVisibility
     * @param Config $catalogConfig
     * @param CurrencyFactory $currencyFactory
     * @param ProductFactory $productFactory
     * @param StoreManagerInterface $storeManager
     * @param ProductCollectionFactory $productCollectionFactory
     * @param AdvancedFactory $advancedFactory
     * @param array $data
     * @param ItemCollectionProviderInterface|null $collectionProvider
     * @param ProductCollectionPrepareStrategyProvider|null $productCollectionPrepareStrategyProvider
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
     */
    public function __construct(
        Context $context,
        Registry $registry,
        AttributeCollectionFactory $attributeCollectionFactory,
        Visibility $catalogProductVisibility,
        Config $catalogConfig,
        CurrencyFactory $currencyFactory,
        ProductFactory $productFactory,
        StoreManagerInterface $storeManager,
        ProductCollectionFactory $productCollectionFactory,
        AdvancedFactory $advancedFactory,
        array $data = [],
        ItemCollectionProviderInterface $collectionProvider = null,
        ProductCollectionPrepareStrategyProvider $productCollectionPrepareStrategyProvider = null
    ) {
        $this->_attributeCollectionFactory = $attributeCollectionFactory;
        $this->_catalogProductVisibility = $catalogProductVisibility;
        $this->_catalogConfig = $catalogConfig;
        $this->_currencyFactory = $currencyFactory;
        $this->_productFactory = $productFactory;
        $this->_storeManager = $storeManager;
        $this->productCollectionFactory = $productCollectionFactory;
        $this->collectionProvider = $collectionProvider;
        $this->productCollectionPrepareStrategyProvider = $productCollectionPrepareStrategyProvider
            ?: ObjectManager::getInstance()->get(ProductCollectionPrepareStrategyProvider::class);
        parent::__construct(
            $context,
            $registry,
            $advancedFactory->create(),
            $this->resolveProductCollection(),
            $data
        );
    }
0

I have assumed that you have overriden the class properly and provided the di.xml in the etc/ folder directly before giving this answer. (Please don't let me down :-))

  1. The problem is coming from the parent class. So started debugging from there.

  2. In your class's constructor you are including a class Magento\CatalogSearch\Model\Search\ItemCollectionProviderInterface($collectionProvider), but when passing the same to the parent, you are explicitly setting it as null. That is the problem here. Try passing the argument $collectionProvider as such to the parent class. Also avoid passing any null values to the parent class via constructor($productCollectionPrepareStrategyProvider).

  3. The variable $productCollectionPrepareStrategyProvider should also have caused the problem in the parent class, but Magento has cleverly used ObjectManager just in case to bypass the error. (Sneaky guy Magento)

  4. To your question about use of ObjectManager::getInstance() in the consturctor, do not use the ObjectManager directly. Try to pass all the required classes via constructor itself. Using ObjectManager will cause your code to fail the Coding Standards required by Magento. Refer - https://devdocs.magento.com/guides/v2.4/coding-standards/code-standard-php.html.

Additional Reference: Refer the file Magento_CatalogSearch/etc/di.xml and search for Magento\CatalogSearch\Model\Advanced to know more about the class arguments that are passed via di.xml. This allows the developers to provide null values for arguments when writing the code for the constructor.

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.