I want to custom sort by option filter attribute add in tool bar section. Currently Magento 2 provide Position , Product Name , Price And Sorting option to low to high. But i need to change all sorting option Like Price High to Low , Price Low to High.
3 Answers
I created these custom sort by options:
- price high - low --> (including all forms of discounts)
- price low - high --> (including all forms of discounts)
- name a-z
- name z-a
- newest
- oldest
Magento 2.4+ (when ElasticSearch is used)
Read full instructions below at 2.4- section but implement changes from this section if you use ElasticSearch (M2.4+)
Main difference when using ElasticSearch instead of MySql is that using a plugin to control Toolbar.php
setCollection
method doesn't seem to work (probably because of ElasticSearch).
But using preference
in di.xml instead works:
<preference for="Magento\Catalog\Block\Product\ProductList\Toolbar" type="Vendor\Module\Block\Product\ProductList\Toolbar" />
namespace Vendor\Module\Block\Product\ProductList;
class Toolbar extends \Magento\Catalog\Block\Product\ProductList\Toolbar
{
public function setCollection($collection)
{
$this->_collection = $collection;
$this->_collection->setCurPage($this->getCurrentPage());
$limit = (int)$this->getLimit();
if ($limit) {
$this->_collection->setPageSize($limit);
}
$currentOrder = $this->getCurrentOrder();
if ($currentOrder) {
if ($currentOrder == 'name_az') {
$this->_collection->setOrder('name', 'asc');
} elseif ($currentOrder == 'name_za') {
$this->_collection->setOrder('name', 'desc');
} elseif ($currentOrder == 'price_asc') {
$this->_collection->setOrder('price', 'asc');
} elseif ($currentOrder == 'price_desc') {
$this->_collection->setOrder('price', 'desc');
} elseif ($currentOrder == 'newest') { // created_at must have "used_for_sort_by = 1" in catalog_eav_attribute table
$this->_collection->setOrder('created_at', 'desc');
} elseif ($currentOrder == 'oldest') {
$this->_collection->setOrder('created_at', 'asc');
} else {
$this->_collection->setOrder($currentOrder, $this->getCurrentDirection());
}
}
return $this;
}
}
Note the comment next to newest
order - if you use newset/oldest sorting by created_at
attribute you need to change the value of the field used_for_sort_by
to 1
in catalog_eav_attribute
table for this attribute.
Also in Magento\Catalog\Model\Config
make sure to put unset($options['created_at']);
since the actual options used are newest/oldest.
Afterwards, just reindex and clear cache.
Magento 2.4- (when Mysql search engine is used)
by creating a plugin to bypass the default functionality of the product sorter which can be found here:
Magento\Catalog\Block\Product\ProductList\Toolbar
and
Magento\Catalog\Model\Config
You start by creating your own module
and registering it.
Then create:
{Vendor}/{ModuleName}/etc/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\Config">
<plugin name="Vendor_ModuleName::addCustomSortOptions" type="Vendor\ModuleName\Plugin\Model\Config" />
</type>
<type name="Magento\Catalog\Block\Product\ProductList\Toolbar">
<plugin name="Vendor_ModuleName::implementCustomSortOptions" type="Vendor\ModuleName\Plugin\Product\ProductList\Toolbar" />
</type>
</config>
Create
{Vendor}/{ModuleName}/Plugin/Model/Config.php
<?php
namespace Vendor\ModuleName\Plugin\Model;
class Config
{
/**
* Adding custom options and changing labels
*
* @param \Magento\Catalog\Model\Config $catalogConfig
* @param [] $options
* @return []
*/
public function afterGetAttributeUsedForSortByArray(\Magento\Catalog\Model\Config $catalogConfig, $options)
{
//Remove default sorting options
unset($options['position']);
unset($options['name']);
unset($options['price']);
//Change label of default sorting options if needed
//$options['position'] = __('Relevance');
//New sorting options
$options['price_desc'] = __('Price High - Low');
$options['price_asc'] = __('Price Low - High');
$options['newest'] = __('Newest');
$options['oldest'] = __('Oldest');
$options['name_az'] = __('Product Name A - Z');
$options['name_za'] = __('Product Name Z - A');
return $options;
}
}
Create
{vendor}/{modulename}/Plugin/Product/ProductList/Toolbar.php
<?php
namespace Vendor\ModuleName\Plugin\Product\ProductList;
class Toolbar
{
/**
* Plugin
*
* @param \Magento\Catalog\Block\Product\ProductList\Toolbar $subject
* @param \Closure $proceed
* @param \Magento\Framework\Data\Collection $collection
* @return \Magento\Catalog\Block\Product\ProductList\Toolbar
*/
public function aroundSetCollection(
\Magento\Catalog\Block\Product\ProductList\Toolbar $toolbar,
\Closure $proceed,
$collection
) {
$this->_collection = $collection;
$currentOrder = $toolbar->getCurrentOrder();
$currentDirection = $toolbar->getCurrentDirection();
$result = $proceed($collection);
if ($currentOrder) {
switch ($currentOrder) {
case 'newest':
$this->_collection
->getSelect()
->order('e.created_at DESC');
break;
case 'oldest':
$this->_collection
->getSelect()
->order('e.created_at ASC');
break;
case 'price_desc':
$this->_collection
->getSelect()
->order('price_index.min_price DESC');
break;
case 'price_asc':
$this->_collection
->getSelect()
->order('price_index.min_price ASC');
break;
case 'name_az':
$this->_collection
->getSelect()
->order('e.name ASC');
break;
case 'name_za':
$this->_collection
->getSelect()
->order('e.name DESC');
break;
default:
$this->_collection
->setOrder($currentOrder, $currentDirection);
break;
}
}
//var_dump((string) $this->_collection->getSelect()); You can use this to get a list of all the available sort fields
return $result;
}
}
-
I followed above steps replace code from {vendor}/{modulename} to our {vendor}/{modulename}. after creating attributes form backend. but it seems no luck. Commented Dec 16, 2017 at 7:26
-
@AnikeshShahi what do you mean "after creating attributes from backend"? This works for sure in Magento 2.2. I suggest you double check if you changed all {vendor} & {modulename} with your own. And make sure you did setup:upgrade, setup:di:compile, clear static files and cache.– LezCommented Dec 17, 2017 at 20:07
-
I already checked, attributes shows in drop down tool bar but not filtering as per selection. Commented Dec 18, 2017 at 5:08
-
@AnikeshShahi if you see the new options, but they don't work, you might have made a mistake in Toolbar.php or maybe you have conflicting modules (which both change the functionality of the toolbar). Maybe you could edit your question with your attempted solution and I could help you locate the mistake.– LezCommented Dec 18, 2017 at 8:40
-
I want to sort the collection by custom price using default sort by price. Have you any solution? @Lez Commented Apr 18, 2018 at 6:04
You have to override \Magento\Catalog\Block\Product\ProductList\Toolbar
and Magento\Catalog\Model\Config
.
etc/di.xml
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Catalog\Block\Product\ProductList\Toolbar" type="{Vendor}\{Module}\Block\Plugin\Product\ProductList\Toolbar" />
<type name="Magento\Catalog\Model\Config">
<plugin name="override-config" type="{Vendor}\{Module}\Model\Plugin\Config" sortOrder="10"/>
</type>
</config>
{vendor}{module}\Block\Plugin\Product\ProductList\Toolbar.php
namespace {Vendor}\{module}\Block\Plugin\Product\ProductList;
class Toolbar extends \Magento\Catalog\Block\Product\ProductList\Toolbar {
public function setCollection($collection) {
$this->_collection = $collection;
$this->_collection->setCurPage($this->getCurrentPage());
$limit = (int) $this->getLimit();
if ($limit) {
$this->_collection->setPageSize($limit);
}
if ($this->getCurrentOrder()) {
switch ($this->getCurrentOrder()) {
case 'created_at':
if ($this->getCurrentDirection() == 'desc') {
$this->_collection
->getSelect()
->order('e.created_at DESC');
} elseif ($this->getCurrentDirection() == 'asc') {
$this->_collection
->getSelect()
->order('e.created_at ASC');
}
break;
case 'in_stock':
$this->_collection
->getSelect()
->joinLeft(
['admin' => $collection->getTable('cataloginventory_stock_item')], 'e.entity_id = admin.product_id')->where('admin.is_in_stock = 1');
break;
case 'out_stock':
$this->_collection
->getSelect()
->joinLeft(
['admin' => $collection->getTable('cataloginventory_stock_item')], 'e.entity_id = admin.product_id')->where('admin.is_in_stock = 0');
break;
default:
$this->_collection->setOrder($this->getCurrentOrder(), $this->getCurrentDirection());
break;
}
}
return $this;
}
}
{Vendor}{Module}\Model\Plugin\Config.php
namespace {Vendor}\{Module}\Model\Plugin;
class Config {
public function afterGetAttributeUsedForSortByArray(
\Magento\Catalog\Model\Config $catalogConfig, $options
) {
$options['created_at'] = __('Newest');
$options['in_stock'] = __('In Stock');
$options['out_stock'] = __('Out of Stock');
return $options;
}
}
-
Small remark. You need join cataloginventory_stock_status not cataloginventory_stock_item table– KAndyCommented Dec 9, 2016 at 14:06
-
Have you any solution for it? i have for
cataloginventory_stock_item
. and its working fine Commented Dec 9, 2016 at 14:22 -
I want to sort the collection by custom price using default sort by price. Have you any solution? @Lez Commented Apr 18, 2018 at 6:05
I did something similar using following code.
app/code/Anshu/Sortby/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Anshu_Sortby',
__DIR__
);
app/code/Anshu/Sortby/etc/module.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Anshu_Sortby" setup_version="1.0.0">
<sequence>
<module name="Magento_Catalog"/>
</sequence>
</module>
</config>
app/code/Anshu/Sortby/etc/di.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magento\Catalog\Block\Product\ProductList\Toolbar" type="Anshu\Sortby\Block\Catalog\Product\ProductList\Toolbar" />
<preference for="Magento\Catalog\Model\Config" type="Anshu\Sortby\Model\Config" />
</config>
app/code/Anshu/Sortby/Model/Config.php
<?php
namespace Anshu\Sortby\Model;
class Config extends \Magento\Catalog\Model\Config
{
/**
* Retrieve Attributes Used for Sort by as array
* key = code, value = name
*
* @return array
*/
public function getAttributeUsedForSortByArray()
{
$options = ['position' => __('Most Popular')];
foreach ($this->getAttributesUsedForSortBy() as $attribute) {
/* @var $attribute \Magento\Eav\Model\Entity\Attribute\AbstractAttribute */
$options[$attribute->getAttributeCode()] = $attribute->getStoreLabel();
}
$options['position'] = __('Most Popular');
$options['price'] = __('Price: Low to High');
$options['price_desc'] = __('Price: High to Low');
return $options;
}
}
app/code/Anshu/Sortby/Block/Catalog/Product/ProductList/Toolbar.php
<?php
namespace Anshu\Sortby\Block\Catalog\Product\ProductList;
use Magento\Catalog\Helper\Product\ProductList;
use Magento\Catalog\Model\Product\ProductList\Toolbar as ToolbarModel;
class Toolbar extends \Magento\Catalog\Block\Product\ProductList\Toolbar
{
/**
* Set collection to pager
*
* @param \Magento\Framework\Data\Collection $collection
* @return $this
*/
public function setCollection($collection)
{
$this->_collection = $collection;
$this->_collection->setCurPage($this->getCurrentPage());
// we need to set pagination only if passed value integer and more that 0
$limit = (int)$this->getLimit();
if ($limit) {
$this->_collection->setPageSize($limit);
}
if ($this->getCurrentOrder()) {
switch ($this->getCurrentOrder()) {
case 'price':
$this->_collection->setOrder($this->getCurrentOrder(), 'ASC');
break;
case 'price_desc':
$this->_collection
->getSelect()
->order('price_index.price DESC');
default:
//$this->_collection->setOrder($this->getCurrentOrder(), $this->getCurrentDirection());
$this->_collection->setOrder($this->getCurrentOrder(), 'DESC');
break;
}
}
return $this;
}
}
I hope this will be helpful.
-
how do you implement most most viewed sorting option please reply ??– PramodCommented Jan 24, 2020 at 12:51
-
@Pramod Check magento.stackexchange.com/questions/194537/… Commented Jan 27, 2020 at 5:47
-
i cant find your ans if you really knows way you can post your and here magento.stackexchange.com/questions/302016/…– PramodCommented Jan 27, 2020 at 5:55