0

I am working in Magento 2.4.1. I need to sort the products on list page and search result page with particular order. I want to display particular products first in all page(list and search). I have custom attribute "Sort". It has two options "Normal" and "Priority". I want to display normal products first everytime in search and list page.

Any simple way to do that ?

I did it with following way in magento 2.3 and it is working well. But in magento 2.4 due to elastic search not working.

I created dynamic module in backend from there user can select category, attribute and set the position. So it will be dynamicaly set. This part does not matter as I tried by static way but still it is not working.

What I did :

di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Catalog\Model\Layer">
        <plugin name="vendor_afterplugin_model_layer" type="vendor\module\Plugin\Layer" sortOrder="3"  />
    </type>
</config>

Layer.php

public function afterGetProductCollection(\Magento\Catalog\Model\Layer $subject, $result_main) {
        // Here I will get the current category and its attribute with position which admin set from backend module
        $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
        $request = $objectManager->get('Magento\Framework\App\Request\Http');
        $_registry = $objectManager->get('\Magento\Framework\Registry');
        $category = $_registry->registry('current_category');
        if (empty($category)) {
            $current_category_id = 0;
        } else {
            $attr_code = $request->getParam('product_list_order');
            if (!isset($attr_code) || empty($attr_code)) {
                $attr_code = $category->getDefaultSortBy();
            }
            $current_category_id = $category->getId();
        }
        $resource = $objectManager->get('Magento\Framework\App\ResourceConnection');
        $connection = $resource->getConnection();
        $tableName = $resource->getTableName('blue42_category_attribute_sort');
        $sql = "Select * FROM " . $tableName . " where category_id=" . $current_category_id;
        $results = $connection->fetchAll($sql);

        $eavModel = $objectManager->create('Magento\Catalog\Model\ResourceModel\Eav\Attribute');

        if (!empty($results)) {
            foreach ($results as $key => $values) {
                $attr = $eavModel->load($values['attribute_id']);
                $attributeCode = $attr->getAttributeCode();
                if (empty($category)) {
                    $attr_code = $attributeCode;
                }
                if ($attr_code == $attributeCode) {
                    $option_value_json = $values['option_id'];
                    $option_value_array = json_decode($option_value_json, true);
                    asort($option_value_array);
                    $order = [];
                    foreach ($option_value_array as $key => $value) {
                        $order[] = $key;
                    }
                    array_push($order, "");
                    $new_array = $result_main->getData();
                    // sort the array based on position
                    if (isset($attr_code)) {
                        usort($new_array, function ($a, $b) use ($order, $attr_code) {
                            $pos_a = array_search($a[$attr_code], $order);
                            $pos_b = array_search($b[$attr_code], $order);
                            return $pos_a - $pos_b;
                        });
                        $id_order = [];
                        foreach ($new_array as $key => $value_id) {
                            $id_order[] = $value_id['entity_id'];
                        }
                        // reset and sort the collection
                        $result_main->getSelect()->reset(\Magento\Framework\DB\Select::ORDER);
                        $result_main->getSelect()->order(new \Zend_Db_Expr("FIELD(e.entity_id, " . implode(",", $id_order) . ")"));
                    }

                }
            }
        }
        return $result_main;
    }
1
  • I have something similar, which sorts based on subcategory position and also sorts based on attribute value position (not attribute value name). Mysql it works fine, ElasticSearch - I've tried using using both afterSetCollection and aroundSetCollection, however I have been unsuccessful at reordering the ElasticSearch results. Did you get any futher?
    – elfling
    Jun 29, 2021 at 0:25

1 Answer 1

0

This doesn't exactly answer your question. It won't help with search results. But I wonder, why don't you make use of the core Magento category product position field since you mention you have custom table that sets position on product level?

enter image description here

For example, here's a simple observer on event catalog_product_save_after that updates the position in each category of the product (based on stock status). Instead, you could set the $position by adding another method to get the data from your custom table or the custom sort order product attribute you mention (I assume a drop down?). Or you could make a cron job to update the category link position e.g. get a collection of updated products in last 24 hours.

public function __construct(
    \Magento\Catalog\Model\ResourceModel\Product\CategoryLink $categoryLink,
    \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
) {
    $this->categoryLink = $categoryLink;
    $this->productRepository = $productRepository;
}

/**
 * @param Observer $observer
 * @return $this
 */
 public function execute(Observer $observer)
 {
   $productId = $observer->getEvent()->getProduct()->getId();
   $product = $this->productRepository->getById($productId);

   $quantityAndStockStatus = $product->getData('quantity_and_stock_status');

   if (isset($quantityAndStockStatus['is_in_stock'])
       && $quantityAndStockStatus['is_in_stock'] == true) {
       $position = 10;
   } else {
       $position = 999;
   }

   $categoryIds = $product->getCategoryIds();
   $categoryLinks = $this->categoryLink->getCategoryLinks($product, $categoryIds);

   foreach ($categoryLinks as $categoryLink) {
     if ($categoryLink['position'] == $position) {
       continue;
     }

     $data = [];
     $data[] = [
       'category_id' => (int)$categoryLink['category_id'],
       'product_id' => (int)$product->getId(),
       'position' => (int)$position,
     ];

     $this->categoryLink->updateCategoryLinks($product, $data);
   }
 }

I'm not sure on the best way to sort the results with elasticsearch, but I think the problem might be the afterGetProductCollection is too late as the data has already been returned from elasticsearch? The other thing I am thinking is that the data you want to sort by is not indexed by elasticsearch.

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.