Demo extension available here https://github.com/ajourquin/module-custom-product-sorting
Default product sorting list
Magento has 4 default sorting options: Name, Price, Position and Relevance.
Sorting are product attributes, except for “position” in catalog pages and “relevance” in search pages
You can add new product attributes to the sorting list by selecting the attribute you want to sort by in admin and set field “Used for Sorting in Product Listing” to “Yes” under “Storefront properties” tab.
Position corresponds to order set by products in category under “Products in Category” section. Can be set manually or via visual merchandising (Magento Commerce only).
Relevance corresponds to score of product for a search.
Default sorting by category
The default product sorting for all categories is set under
Stores > Configuration > Catalog > Catalog > Storefront > Product Listing Sort by
You can choose a different default product sorting by category.
Set “Default Product Listing Sort By” to the sorting you want as default under “Display Settings” in an edit category page in admin
Add custom sorting
1- Add the custom value in frontend sorting list
\Magento\Catalog\Model\Config::getAttributeUsedForSortByArray is responsible to populate the product sorting dropdown
So to add your custom value just create an after plugin afterGetAttributeUsedForSortByArray

2- Add the custom value in config sorting list
\Magento\Catalog\Model\Config\Source\ListSort::toOptionsArray is responsible to populate the product sorting dropdown.
Stores > Configuration > Catalog > Catalog > Storefront > Product Listing Sort by
Create an after plugin afterToOptionArray to add the custom value

Now you are able to select the Custom Value sorting by default for all categories
3- Add the custom value in category edit page
\Magento\Catalog\Model\Category\Attribute\Source\Sortby::getAllOptions is responsible to populate the dropdown for “Default Product Listing Sort By” field
Create an after plugin afterGetAllOptions to add the custom value

Now you are able to select the Custom Value sorting by default for a specific category
4- Sort products
There are different places where you can sort products collection using plugins.
You can use a before plugin on \Magento\Catalog\Model\Layer::prepareProductCollection in frontend area

That’s it!
— Update 2020-12-29 —
In order to make it compatible with Magento 2.4 and ElasticSearch you need to create an afterResolve plugin on \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolver to remove the ‘custom_value‘ (which is not a real attribute sorting value) from the SearchCriteria sort orders
Also you have to add the field you want to order on in the ES index or implement your own SearchResultApplier.
See \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier as reference
February 25, 2020 at 2:59 AM
Really appreciate this, worked a treat! Thanks 🙏
February 25, 2020 at 3:02 AM
Ps. I chose to implement the actual sorting logic in the Toolbar, e.g.
“`
namespace ….\Catalog\Block;
class Toolbar
{
public function aroundSetCollection(
\Magento\Catalog\Block\Product\ProductList\Toolbar $subject,
\Closure $proceed,
$collection
) {
$currentOrder = $subject->getCurrentOrder();
$result = $proceed($collection);
if ($currentOrder) {
if ($currentOrder == ‘price_desc’) {
$subject->getCollection()->setOrder(‘price’, ‘desc’);
} elseif ($currentOrder == ‘price_asc’) {
$subject->getCollection()->setOrder(‘price’, ‘asc’);
} elseif ($currentOrder == ‘newest’) {
$subject->getCollection()->setOrder(‘created_at’, ‘desc’);
} elseif ($currentOrder == ‘name_asc’) {
$subject->getCollection()->setOrder(‘name’, ‘asc’);
} elseif ($currentOrder == ‘name_desc’) {
$subject->getCollection()->setOrder(‘name’, ‘desc’);
}
}
return $result;
}
“`
March 2, 2020 at 11:10 AM
You should use an after plugin instead an around plugin as a best practice. You can get function parameters with after plugins since version 2.2
July 7, 2020 at 8:57 AM
Really Admirable…! it helps me a lot … Thank you so much 🙂
November 1, 2020 at 1:52 AM
Your solution is perfectly fine working with MySql option but not working with Elasticsearch. Any ideas? I tried to implement this on magento 2.4 with no luck.
November 1, 2020 at 8:25 PM
I haven’t had a chance to look for 2.4 yet and ES 🙁
November 5, 2020 at 9:33 PM
I’ve checked this point and the fact is the ‘custom_value’ sort is added with the others one (price and name in our case) to the request sent to ES.
The idea is just to remove the non real sort before the request is sent.
You can create a plugin of Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolver:
public function afterResolve(MagentoSearchCriteriaResolver $subject, SearchCriteria $result): SearchCriteria
{
$sortOrders = $result->getSortOrders();
unset($sortOrders['custom_value']);
$result->setSortOrders($sortOrders);
return $result;
}
December 24, 2020 at 5:49 AM
I’ve tried this by creating a plugin for Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchCriteriaResolver in Magento 2.3.5-p1 and 2.4.x but it does nothing with the products collection.
The products are displaying as same as it displaying for position sorting after unset the custom sort variable in the plugin class.
Any help will be appreciated.
December 25, 2020 at 7:09 PM
Yet I can make it work with this solution. Try the updated version https://github.com/ajourquin/module-custom-product-sorting
December 28, 2020 at 12:44 AM
Thank you for your reply!
I’ve tried this module with my custom code for collection but the results are same as it displaying for position sorting.
Below is my code for custom sort I’ve added in the Layer.php file.
$collection->getSelect()->joinLeft(
‘report_event’,
‘e.entity_id = report_event.object_id’,
[‘view_count’ => ‘COUNT(report_event.event_id)’]
)
->group(‘e.entity_id’)
->order(‘view_count DESC’);
Is there anything I need to add or update in the extension?
Thanks
December 29, 2020 at 10:05 PM
Just tried and it works on 2.3.5.
If you’re trying on 2.4 you should add the view count to the index or implement your own SearchResultApplier.
See \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier as reference
December 29, 2020 at 4:38 AM
How to sort by newest products.
$collection->setOrder(‘e.created_at’, ‘desc’); OR
$collection->setOrder(‘created_at’, ‘desc’);
Not working.
If you have any idea please let me know
December 29, 2020 at 10:05 PM
Just tried and it works on 2.3.5.
If you’re trying on 2.4 you should add the created_at to the index or implement your own SearchResultApplier.
See \Magento\Elasticsearch\Model\ResourceModel\Fulltext\Collection\SearchResultApplier as reference
December 30, 2020 at 9:43 AM
I just thought let’s give this a try and it works. But Should I do this change in the database table or there is a better way to do this?
# Get the attribute_id of ‘created_at’
select attribute_id from eav_attribute where attribute_code = ‘created_at’ and entity_type_id=4;
# Set frontend_label
update eav_attribute set frontend_label = ‘Created At’ where attribute_id=112;//default Null
# Set used_for_sort_by
update catalog_eav_attribute set used_for_sort_by = 1 where attribute_id=112;//default 0
December 30, 2020 at 12:18 PM
IMO as created_at is a product attribute it’s better to use the Magento native feature ‘Used for sort by’.
Attribute will be automatically added to the index and ready to use