303.473.4400 / Toll Free: 888.772.0777
Select Page

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.

 
screen-shot-2016-06-09-at-5-49-26-pm
 

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 sorting the catalog by 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

I’m going to go through the process that I took to figure this out. While this might seem a little long-winded, I want to go over all the different parts that make up this functionality, to hopeful lend a better understanding of the whole system. Let’s 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 override.

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 be looking at the 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/code/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.

Preamble over, lets start looking at the code. The tool bar is rendered onto the page using a phtml file. That file is found here: vendor/magento/module-catalog/view/frontend/templates/product/list/toolbar.phtml. Inside you will see the call to the sorter file inside a if statement:

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

If this looks new to you, the function getTemplateFile() is just a utility function that will call in whatever template you specify (this same thing can also be done in xml). Magento is using it’s short hand notation for that file, but the template call resolves to this: 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 head spinning 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)); ?>

By placing this in the frontend template (vendor/magento/module-catalog/view/frontend/templates/product/list/toolbar.phtml) and refreshing the page we will see a large list of block methods that can be called on the block.

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 started. 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. I didn’t make this up, or invent the methods we are using here, this is a part of Magento’s magic setters and getters system. That system is much too much to get into with this post, but just for starters just understand that if a method exists in the system, Magento will automatically create getter and setters for that method. But this can be confusing for new developers, as the normal way of searching a code base will not return all of the possible solutions for a problem.

What this means though, is that looking through the core and knowing a few basic concepts about the .xml structure Magento uses will open up an incredible amount of control, control that in some cases no one has used yet.

While it might be likely that this problem will be solved in new updates of Magento 2, you now have the power to control it even if it isn’t. And having these little bit of knowledge will get you a fast solution for your clients!

Pin It on Pinterest

Share This