Changing Default Product Sorting in Magento 2

Recently, one of our clients wanted to know if we could change the default sorting direction for the product list on their site. In Magento 2, the default sorting order is ascending, but what if you would like to set it to descending by default? While it would seem like this would be a simple admin configuration, you actually have to edit a .xml file to get this functionality.

In this tutorial, I’m going to walk you through the process of changing the default sorting direction of your product list.

In the admin under Stores > Config > Catalog > Catalog > Storefront > Product Listing Sort By you can change the default method Magento uses to sort your catalog, giving the store owner the ability to focus on their ideal presentation for the users first impression for their catalog. This defaults to Position, taking the numeric weight set for each individual product. For Price, the least expensive product will sort to the top, which is rarely what most store owners would want. Our client was hoping to have the most expensive products show up above the fold.

How to Change the Default Product Sorting

Lets take a trip through the core code and see how to change this.

I will assume that you already have a fully working Magento 2 install on a development environment (say it with me ‘I will never develop on a production site’) with some sort of sample data for testing. I will also assume that you have a theme installed where you can place the needed file overrides.

For this tutorial I’m using a theme that is inheriting from the default Luma theme, but this does not matter for your application, as the sorting toolbar is default Magento functionality.

We will also will be looking at the core files that make up Magento 2 (commonly called “core files”). Depending on how you installed Magento 2, your core files that we will be looking at can be in two different location, under vendor/magento/ or app/design/frontend/Magento/. For this tutorial, my core files I will be referencing are in vendor/magento/ but the override file names will be the same no matter where Magento installed them. If you are new to Magento it’s worth some time looking into these files and getting a working understanding of the structure of the system. Magento is a huge and complex system that takes time to learn and understand. Getting a good understanding of what is where is the first step in learning the system as a whole.

The file that calls the catalog tool bar is found here /vendor/magento/module-catalog/view/frontend/templates/product/list/toolbar.phtml, inside you will see the call to the sorter file:

<?php if ($block->isExpanded()): ?>
    <?php include ($block->getTemplateFile('Magento_Catalog::product/list/toolbar/sorter.phtml')) ?>
<?php endif; ?>

Which lives here /vendor/magento/module-catalog/view/frontend/templates/product/list/toolbar/sorter.phtml. In this file, you have the check against the getter method that retrieves the default sorting value:

$block->getCurrentDirection();

Looking at the class definition of this method (\Magento\Catalog\Block\Product\ProductList\Toolbar found in this file /vendor/magento/module-catalog/Block/Product/ProductList/Toolbar.php) we see:

    
/**
 * Retrieve current direction
 *
 * @return string
 */
public function getCurrentDirection()
{
    $dir = $this->_getData('_current_grid_direction');
    if ($dir) {
        return $dir;
    }

    $directions = ['asc', 'desc'];
    $dir = strtolower($this->_toolbarModel->getDirection());
    if (!$dir || !in_array($dir, $directions)) {
        $dir = $this->_direction;
    }

    if ($dir != $this->_direction) {
        $this->_memorizeParam('sort_direction', $dir);
    }

    $this->setData('_current_grid_direction', $dir);
    return $dir;
}

This code first checks _getData() to see if the direction is set there. If it’s not it calls the method getDirection() on the _toolbarModel(). Following down the rabbit hole, we find that the getDirection() method checks a constant set in the Model (/vendor/magento/module-catalog/Model/Product/ProductList/Toolbar.php). If the direction is not set there, it checks against a constant that’s set in the Helper (/vendor/magento/module-catalog/Helper/Product/ProductList.php) with $this->_direction. My guess is this structure is in place to set up an admin configuration for the default sorting order that hasn’t made it’s way into the current release.

With this structure in mind, what we are looking for is a method for setting that default sorting order. When working on a problem like this, I like to check what methods the block has available to me.

<?php var_dump(get_class_methods($block)); ?>

Will show you a large list of block methods that can be called. Inside we find this Method:

/**
 * Set default sort direction
 *
 * @param string $dir
 * @return $this
 */
public function setDefaultDirection($dir)
{
    if (in_array(strtolower($dir), ['asc', 'desc'])) {
        $this->_direction = strtolower($dir);
    }
    return $this;
}

This looks very promising, but it can’t be called directly on the $block object. Instead, we are going to use the configuration .xml file to call in the Block class and set the value there.

After that long digression, we can now get to action. In your theme, create a file app/design/frontend/{{Vendor_Namespace}}/{{Theme_Name}}/Magento_Catalog/layout/catalog_category_view.xml. In this file copy the code below (if you already have this file in your theme, you may already have the xml version, body and referenceContainer name="content". If that is the case, you just need to add in the referenceBlock to your code).

   
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-left" xsi:nonamespaceschemalocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    
    <referencecontainer name="content">
        <referenceblock class="Magento\Catalog\Block\Product\ListProduct" name="category.products.list" as="product_list">
            <action method="setDefaultDirection">
                <argument name="dir" xsi:type="string">desc</argument>
            </action>
        </referenceblock>
    </referencecontainer>
    
</page>

In this code, we can call the method from the Block. For those with experience in Magento 1, this will be slightly familiar to you, but there are some very large differences from a M1 implementation. Now we can use a very unambiguous call on the Class we want to use, which is a great improvement over the M1 way of calling Blocks in the .xml files.

Let’s break down this code. We are referencing the main content container of the catalog_category_view page. Inside that node we have the call to the Block Class that has the method we are looking to set, and we call that Method by using the <action method="setDefaultDirection"></action> node.

If that syntax looks a little confusing, this is one of those places in Magento development where you have to ‘just get use to it’. What you are looking at is the basic equivalent to $block->setDefaultDirection(). From there we have to pass in the value we need to set.

Looking back at the setDefaultDirection($dir) we see that a value needs to be passes as a string. The .xml version of this argument is desc. The node defines all the things we need Magento to know, the name we are giving it (which is arbitrary, but should follow some simple naming convention), the data type of the value (in our case, a string) and the value itself.

If you save the file and refresh your catalog page, you should see the sorting arrow now descending by default. If you don’t see the change, make sure to clear your cache.

Closing Thoughts

I hope this gives just a small new appreciation of the power of the .xml configuration system that Magento has. As a developer, it gives you the ability to make massive changes to the structure of a site with just a few lines of code.

If you grep the code you see above (grep -r "setDefaultDirection"), you aren’t going to find a .xml reference to the code we just wrote in the core code. What this means is that looking though the core and knowing a few basic concepts about the .xml structure Magento uses will open up an incredible amount of control from your theme, control that no one has used yet. If you had be wondering why we took such a long detour into the core code to find what methods we needed to use to get this functionality working, this is why. From looking over the code we can find a solution that is new to us and the framework.

It is very likely in new updates of Magento 2 that this setting might be pulled into the admin, since it is such a common request. But till then, this will get you a fast solution for your clients.