AfterGetProductCollection plugin hassle with SearchResultApplier

  I wrote an AfterGetProductCollection plugin for Magento\CatalogSearch\Model\Advanced updating the resulting collection. I am using Elasticsearch and Magento Commerce. The problem is that Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier is not aware of the collection updates and applies the original search results product ids.

Ah, I see! You're working on a plugin to modify the product collection in Magento with Elasticsearch. To address the issue with the SearchResultApplier not being aware of the collection updates, you may need to extend the SearchResultApplier itself.

You can create a custom module and use dependency injection (DI) to override the original SearchResultApplier class. In your custom module's di.xml file, you can add a preference to point to your new class that extends the original SearchResultApplier. This way, Magento will use your custom class instead of the original one.

Here's an example for your di.xml file:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier" type="YourVendor\YourModule\Model\ResourceModel\Fulltext\Collection\CustomSearchResultApplier" />
</config>

In your new CustomSearchResultApplier class, you can extend the original SearchResultApplier and override the necessary method(s) to apply the search results based on your updated product collection.

Does this help you with the issue you're facing?

I know this, but how should we update the search collection for SearchResultApplier when we updated the search result collection with an AfterGetProductCollection plugin for Magento\CatalogSearch\Model\Advanced ?

I understand now. Since you're using an AfterGetProductCollection plugin for Magento\CatalogSearch\Model\Advanced, you need to update the search collection for SearchResultApplier as well.

First, you should create a custom module, if you haven't already, and make sure your AfterGetProductCollection plugin is working as expected.

Next, you'll need to create another plugin, this time targeting the Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier. You'll want to use the aroundApply method to modify the behavior of the original apply method.

Here's an example of how your plugin class might look:

namespace YourVendor\YourModule\Plugin;

class SearchResultApplierPlugin
{
    public function aroundApply(
        \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier $subject,
        callable $proceed,
        \Magento\Framework\Data\Collection\AbstractDb $collection
    ) {
        // Your logic to update the search collection based on your modified product collection.
        // You might need to store and retrieve the updated product collection from your AfterGetProductCollection plugin.

        // Call the original apply method with the updated search collection.
        $result = $proceed($collection);

        return $result;
    }
}

In your custom module's di.xml file, you will need to add the plugin:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier">
        <plugin name="your_vendor_your_module_search_result_applier_plugin" type="YourVendor\YourModule\Plugin\SearchResultApplierPlugin" sortOrder="10" />
    </type>
</config>

This way, your custom SearchResultApplierPlugin will be able to update the search collection based on the changes you made in your AfterGetProductCollection plugin.

Does this approach make sense to you and help with your issue?

But the apply method from Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier using Magento\Framework\Api\Search\SearchResultInterface to apply pagination and sorting or ordering values. How do I resolve in this case ordering and pagination values?

I see, in that case, you'll have to modify the Magento\Framework\Api\Search\SearchResultInterface instance that is being used by the SearchResultApplier to apply the pagination and sorting values.

To achieve this, you can use the following approach:

  1. In your AfterGetProductCollection plugin, store the updated product collection somewhere that can be accessed by your SearchResultApplierPlugin. You can use a custom helper class with a getter and a setter for this purpose.

  2. Create another plugin for the Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier class, targeting the apply method, as mentioned in the previous response.

  3. In the aroundApply method of your SearchResultApplierPlugin, retrieve the updated product collection from the custom helper class.

  4. Now, create a new instance of SearchResultInterface using the Object Manager or Dependency Injection, and set the updated product collection as its items.

  5. Update the pagination and sorting values in your new SearchResultInterface instance based on the updated product collection.

  6. Finally, call the original apply method of the SearchResultApplier using the new SearchResultInterface instance with the updated product collection, pagination, and sorting values.

Here's an example of how your SearchResultApplierPlugin class might look:

namespace YourVendor\YourModule\Plugin;

class SearchResultApplierPlugin
{
    protected $helper;

    public function __construct(
        \YourVendor\YourModule\Helper\Data $helper
    ) {
        $this->helper = $helper;
    }

    public function aroundApply(
        \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier $subject,
        callable $proceed,
        \Magento\Framework\Api\Search\SearchResultInterface $searchResult
    ) {
        // Retrieve the updated product collection from your custom helper class.
        $updatedProductCollection = $this->helper->getUpdatedProductCollection();

        // Create a new SearchResultInterface instance and set the updated product collection as its items.
        $newSearchResult = clone $searchResult;
        $newSearchResult->setItems($updatedProductCollection->getItems());

        // Update the pagination and sorting values based on the updated product collection.
        // ...

        // Call the original apply method with the new SearchResultInterface instance.
        $result = $proceed($newSearchResult);

        return $result;
    }
}

This way, you should be able to resolve the ordering and pagination values based on your updated product collection.

Does this approach help you address your issue?

There is a problem, the apply method has no parameters so the aroundApply( \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier $subject, callable $proceed, \Magento\Framework\Api\Search\SearchResultInterface $searchResult ) plugin will not work only if you declare as public function aroundApply( \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier $subject, callable $proceed )

I apologize for the confusion. You're right, the apply method doesn't have any parameters. In that case, we need to adjust the approach for the aroundApply method in the SearchResultApplierPlugin.

Here's an updated version of the SearchResultApplierPlugin:

namespace YourVendor\YourModule\Plugin;

class SearchResultApplierPlugin
{
    protected $helper;

    public function __construct(
        \YourVendor\YourModule\Helper\Data $helper
    ) {
        $this->helper = $helper;
    }

    public function aroundApply(
        \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier $subject,
        callable $proceed
    ) {
        // Retrieve the updated product collection from your custom helper class.
        $updatedProductCollection = $this->helper->getUpdatedProductCollection();

        // Get the search result from the SearchResultApplier instance.
        $searchResult = $subject->getSearchResult();

        // Create a new SearchResultInterface instance and set the updated product collection as its items.
        $newSearchResult = clone $searchResult;
        $newSearchResult->setItems($updatedProductCollection->getItems());

        // Update the pagination and sorting values based on the updated product collection.
        // ...

        // Set the new SearchResultInterface instance in the SearchResultApplier.
        $subject->setSearchResult($newSearchResult);

        // Call the original apply method.
        $result = $proceed();

        return $result;
    }
}

With this adjusted aroundApply method, we're getting the search result directly from the SearchResultApplier instance and updating it with the new product collection, pagination, and sorting values before calling the original apply method.

I hope this updated approach helps you with your issue. Please let me know if you have any questions or need further clarification.

How do I create in Helper/Data the setter and getter methods for the collection?

In your custom module, you can create a Helper/Data.php file to store the setter and getter methods for the updated product collection. Here's an example of how your Helper/Data.php file might look:

namespace YourVendor\YourModule\Helper;

use Magento\Framework\App\Helper\AbstractHelper;

class Data extends AbstractHelper
{
    protected $updatedProductCollection;

    /**
     * Set the updated product collection.
     *
     * @param \Magento\Catalog\Model\ResourceModel\Product\Collection $collection
     * @return void
     */
    public function setUpdatedProductCollection(\Magento\Catalog\Model\ResourceModel\Product\Collection $collection)
    {
        $this->updatedProductCollection = $collection;
    }

    /**
     * Get the updated product collection.
     *
     * @return \Magento\Catalog\Model\ResourceModel\Product\Collection|null
     */
    public function getUpdatedProductCollection()
    {
        return $this->updatedProductCollection;
    }
}

In this helper class, we have two methods:

  1. setUpdatedProductCollection: This method accepts a \Magento\Catalog\Model\ResourceModel\Product\Collection object as a parameter, and sets it as the value of the $updatedProductCollection property.

  2. getUpdatedProductCollection: This method returns the stored value of the $updatedProductCollection property. If the property hasn't been set yet, it will return null.

In your AfterGetProductCollection plugin, you can use the setUpdatedProductCollection method to store the updated product collection, and in your SearchResultApplierPlugin, you can use the getUpdatedProductCollection method to retrieve the stored collection.

Does this help you create the setter and getter methods for the updated product collection? Let me know if you need further clarification or have any questions.

The setter method is enough to store the the updated product collection in the helper?

Yes, the setUpdatedProductCollection method in the helper is enough to store the updated product collection. By using this method, you'll be setting the value of the $updatedProductCollection property in the helper class.

Here's an example of how to use the helper in your AfterGetProductCollection plugin:

namespace YourVendor\YourModule\Plugin;

class AdvancedPlugin
{
    protected $helper;

    public function __construct(
        \YourVendor\YourModule\Helper\Data $helper
    ) {
        $this->helper = $helper;
    }

    public function afterGetProductCollection(
        \Magento\CatalogSearch\Model\Advanced $subject,
        \Magento\Catalog\Model\ResourceModel\Product\Collection $result
    ) {
        // Your logic to update the product collection.
        // ...

        // Store the updated product collection in the helper.
        $this->helper->setUpdatedProductCollection($result);

        return $result;
    }
}

Then, in your SearchResultApplierPlugin, you can use the getUpdatedProductCollection method to retrieve the stored updated product collection:

// Retrieve the updated product collection from your custom helper class.
$updatedProductCollection = $this->helper->getUpdatedProductCollection();

By using the helper's setter and getter methods, you can easily store and retrieve the updated product collection across your plugins.

Let me know if you have any questions or need further clarification.

There is a problem there is no getSearchResult or setSearchResult method in \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier and also $searchResult is private

I apologize for the confusion. In this case, we'll need to adjust our approach again to work with the private $searchResult property in the SearchResultApplier class.

Since you can't access the $searchResult property directly or through a getter/setter method, you can create a custom class that extends the SearchResultApplier class and adds your desired functionality.

  1. Create a custom class that extends Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier.

  2. Override the apply method in your custom class to update the search result with the updated product collection and pagination/sorting values.

  3. Use the di.xml file in your custom module to replace the original SearchResultApplier class with your custom class.

Here's an example of your custom SearchResultApplier class:

namespace YourVendor\YourModule\Model\ResourceModel\Fulltext\Collection;

class CustomSearchResultApplier extends \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier
{
    protected $helper;

    public function __construct(
        \YourVendor\YourModule\Helper\Data $helper,
        \Magento\Framework\Api\Search\SearchResultInterface $searchResult,
        \Magento\Framework\Data\Collection\AbstractDb $collection
    ) {
        $this->helper = $helper;
        parent::__construct($searchResult, $collection);
    }

    public function apply()
    {
        // Retrieve the updated product collection from your custom helper class.
        $updatedProductCollection = $this->helper->getUpdatedProductCollection();

        // Update the search result with the updated product collection.
        // ...

        // Update the pagination and sorting values based on the updated product collection.
        // ...

        return parent::apply();
    }
}

In your custom module's di.xml file, add a preference to replace the original SearchResultApplier class with your custom class:


Comments

Popular posts from this blog

References and blocks

The Object Manager and the Dependency Injection

How to sanitize request in a magento 2 controller?