How to Add Custom Sorting in Category of Magento 2.4 and Later

As we all know after the release of Magento 2.4 it uses elastic search and due to elastic search, some old custom sorting-related solutions are not working. So, to overcome this issue we have written this article. Most of the time our client wants to add custom sorting options on their website and most of the developers are looking for a solution for that. So, we have written this article for those developers who are looking for a solution to add custom filters on the category page.

In this article, we will create custom out of stock sorting in the category listing page. So, with the help of this article you also know how to display out of stock product at the end of the product list Page. You can sort it by ACS and DESC like other Magento sorting.

If you are looking for a solution for Magento 2.3 or before then explore our another article which is How to Add Custom Sorting in Category of Magento 2

Before we start I assume, you have already a created custom module. If you don't have it or don't know how to create it then check out our other article How To Create a Magento 2 Module.

Let's get started!!

1. Create a di.xml file in a module at app/code/Devhooks/CustomSorting/etc/ and paste below code inside the file.

saveCopyzoom_out_map
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <type name="Magento\Catalog\Model\Config">
		<plugin name="catalog_config_plugin" type="Devhooks\CustomSorting\Plugin\Config"/>
	</type>
    <type name="Magento\Elasticsearch\Model\Adapter\BatchDataMapper\ProductDataMapper">
        <plugin name="add_data_to_mappers_result" type="Devhooks\CustomSorting\Plugin\Model\Adapter\BatchDataMapper\ProductDataMapperPlugin"/>
    </type>
</config>

In above, Devhooks is a Namespace name and HelloWorld is a Module name. So, please don't forget to change it with your Namespace and Module Name.

Also, you can see we've define 2 plugins. So, now we'll create that.

2. Create a Config.php file in a module at app/code/Devhooks/CustomSorting/Plugin/ and paste below code inside the file.

saveCopyzoom_out_map
<?php

namespace Devhooks\CustomSorting\Plugin;

class Config
{
    public function afterGetAttributeUsedForSortByArray(\Magento\Catalog\Model\Config $catalogConfig, $options)
    {
        $newOptions = ['stock' => __('Out of Stock')];
        $options = array_merge($options, $newOptions);
        return $options;
    }
}

In above plugin we've added sort by name which will display in sort by dropdown. You can change it as per your need.

3. Create a ProductDataMapperPlugin.php file in a module at app/code/Devhooks/CustomSorting/Plugin/Model/Adapter/BatchDataMapper/ and paste below code inside the file.

saveCopyzoom_out_map
<?php

namespace Devhooks\CustomSorting\Plugin\Model\Adapter\BatchDataMapper;

use Magento\Elasticsearch\Model\Adapter\BatchDataMapper\ProductDataMapper;
use Devhooks\CustomSorting\Model\Elasticsearch\Adapter\DataMapper\AttributesToSort as AttrDataMapper;
use Devhooks\CustomSorting\Model\ResourceModel\Inventory;
use Magento\Framework\Exception\NoSuchEntityException;

class ProductDataMapperPlugin
{
    protected $attrDataMapper;
    protected $inventory;

    public function __construct(AttrDataMapper $attrDataMapper, Inventory $inventory)
    {
        $this->attrDataMapper = $attrDataMapper;
        $this->inventory = $inventory;
    }

    public function afterMap(
        ProductDataMapper $subject,
        $documents,
        $documentData,
        $storeId,
        $context
    ) {
        $this->inventory->saveRelation(array_keys($documents));

        foreach ($documents as $productId => $document) {
            //@codingStandardsIgnoreLine
            $document = array_merge($document, $this->attrDataMapper->map($productId, $storeId));
            $documents[$productId] = $document;
        }

        $this->inventory->clearRelation();

        return $documents;
    }
}

In above code, you can see we've used 2 models files which will use for customize/filter collection.

As I mention above we'll create out of stock sorting so I'll create model file for to check products stock status. You need to create model files as per your need.

4. Create a Inventory.php file in a module at app/code/Devhooks/CustomSorting/Model/ResourceModel/ and paste below code inside the file.

saveCopyzoom_out_map
<?php

namespace Devhooks\CustomSorting\Model\ResourceModel;

use Magento\CatalogInventory\Api\StockRegistryInterface;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
use Magento\Framework\Model\ResourceModel\Db\Context;
use Magento\Framework\Module\Manager;

class Inventory extends AbstractDb
{
    private $moduleManager;
    private $stockRegistry;
    private $stockStatus;
    private $stockIds;
    private $skuRelations;

    public function __construct(
        Manager $moduleManager,
        StockRegistryInterface $stockRegistry,
        Context $context,
        $connectionName = null
    ) {
        parent::__construct($context, $connectionName);
        $this->moduleManager = $moduleManager;
        $this->stockRegistry = $stockRegistry;
    }

    protected function _construct()
    {
        $this->stockIds = [];
        $this->skuRelations = [];
    }

    public function getStockStatus($productSku, $websiteCode): int
    {
        if ($this->moduleManager->isEnabled('Magento_Inventory')) {
            $stockStatus = $this->getMsiStock($productSku, $websiteCode);
        } else {
            $stockStatus = $this->stockRegistry
                ->getStockItemBySku($productSku, $websiteCode)
                ->getIsInStock();
        }

        return (int)$stockStatus;
    }

    protected function getMsiStock(string $productSku, string $websiteCode): int
    {
        if (!isset($this->stockStatus[$websiteCode][$productSku])) {
            $select = $this->getConnection()->select()
                ->from($this->getTable('inventory_stock_' . $this->getStockId($websiteCode)), ['is_salable'])
                ->where('sku = ?', $productSku)
                ->group('sku');
            $this->stockStatus[$websiteCode][$productSku] = (int) $this->getConnection()->fetchOne($select);
        }

        return $this->stockStatus[$websiteCode][$productSku];
    }

    public function getStockId(string $websiteCode)
    {
        if (!isset($this->stockIds[$websiteCode])) {
            $select = $this->getConnection()->select()
                ->from($this->getTable('inventory_stock_sales_channel'), ['stock_id'])
                ->where('type = \'website\' AND code = ?', $websiteCode);

            $this->stockIds[$websiteCode] = (int)$this->getConnection()->fetchOne($select);
        }

        return $this->stockIds[$websiteCode];
    }

    public function saveRelation(array $entityIds): Inventory
    {
        $select = $this->getConnection()->select()->from(
            $this->getTable('catalog_product_entity'),
            ['entity_id', 'sku']
        )->where('entity_id IN (?)', $entityIds);

        $this->skuRelations = $this->getConnection()->fetchPairs($select);

        return $this;
    }

    public function clearRelation()
    {
        $this->skuRelations = null;
    }
    
    public function getSkuRelation(int $entityId): string
    {
        return $this->skuRelations[$entityId] ?? '';
    }
}

5. Create a AttributesToSort.php file in a module at app/code/Devhooks/CustomSorting/Model/Elasticsearch/Adapter/DataMapper/ and paste below code inside the file.

saveCopyzoom_out_map
<?php

namespace Devhooks\CustomSorting\Model\Elasticsearch\Adapter\DataMapper;

use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Store\Model\StoreManagerInterface;
use Devhooks\CustomSorting\Model\ResourceModel\Inventory;

class AttributesToSort
{
    private $inventory;
    private $storeManager;

    public function __construct(
        Inventory $inventory,
        StoreManagerInterface $storeManager
    ) {
        $this->inventory = $inventory;
        $this->storeManager = $storeManager;
    }

    public function map($entityId, $storeId)
    {
        $attrToSort = [];
        $sku = $this->inventory->getSkuRelation((int)$entityId);
        if ($sku) {
            $websiteCode = $this->storeManager->getStore($storeId)->getWebsite()->getCode();
            $attrToSort['stock'] = $this->inventory->getStockStatus($sku, $websiteCode);
        } else {
            $attrToSort['stock'] = 0;
        }

        return $attrToSort;
    }
}

In above code, you can see we've used $attrToSort['stock']. And also we've defined $newOptions = ['stock' => __('Out of Stock')]; in the 2 step. So, Make sure this array key should be same in a both files.

6. Open Command line in folder root of magento and run the below command.

saveCopyzoom_out_map
php bin/magento cache:clean

Now, go to the category listing page and check. You will see out of stock option is added in sory by dropdown like the below screenshort. How to Add Custom Sorting in Category of Magento 2.4 and Later

With he help of this article you can display out of stock products at the end of the catalog in Magento 2 usgin custom sorting.

That’s it! Now visit your site and navigate to category page, and check new sorting option.

I hope this Magento article helped you to find what you were looking for.

Bookmark it for your future reference. Do comment below if you have any other handy commands which are not included in the list.

P.S. Do share this note with your team.




Search
Recent Articles
Tags
Newsletter
Copyright © 2022 devhooks.in All rights reserved.
Ads OFF toggle_off
wifi_off