Randomize the product list in all categories

Recently I had a request from a customer asking to randomize the product listing in all the categories. The goal was to give the illusion of “freshness” or frequent changes in the categories.

To achieve this the product collection created when a category is called needed to be sorted in a random order. I created an extension to override the Toolbar class which is responsible to create that product collection based on the Toolbar options.

To override a class in magento 2 in a custom module we need to set a preference for that class in the di.xml file located in the /etc folder of the extension.

<?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="Webguru\RandomProductList\Block\Product\ProductList\Toolbar" />
</config>

The preference statement tells magento to replace the Magento\Catalog\Block\Product\ProductList\Toolbar class with our custom class build in this module which is called Webguru\RandomProductList\Block\Product\ProductList\Toolbar

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

namespace Webguru\RandomProductList\Block\Product\ProductList;

/**
 * Product list
 * @api
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
 * @since 100.0.2
 */
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()) {
            if (($this->getCurrentOrder()) == 'position') {
                $this->_collection->addAttributeToSort(
                    $this->getCurrentOrder(),
                    $this->getCurrentDirection()
                );
            } else {
                $this->_collection->setOrder($this->getCurrentOrder(), $this->getCurrentDirection());
            }
        }
		
		// RANDOMIZE using a seed that changes every day
		$seed = date("Ymd");
		$this->_collection->getSelect()->order('rand('.$seed.')');
		// END of RANDOMIZE code
		
        return $this;
    }
}

The override is done by extending the magento Toolbar class with our custom build Toolbar class. By implementing the method setCollection in our custom Toolbar class we are replacing the original code, meaning that whenever magento call this method anywhere in the application, our customer setCollection method will be called instead of the original setCollection method implemented in the magento Toolbar class.

The code in the setCollection for the custom Toolbar class is a copy of the original method with some additional statements at the end to implement the randomization.

// RANDOMIZE using a seed that changes every day
$seed = date("Ymd");
$this->_collection->getSelect()->order('rand('.$seed.')');
// END of RANDOMIZE code

These 2 lines create a seed for the random generation process and use it to set the order for the product collection. In the above example the seed is being create using the date php function that returns a number by concatenating the current year, month and day. This way, the seed changes everyday making the product collection order to be changed every 24h.

You can download the extension for free for my GitHub repository.

Although I used the preference method to override this same result could have also been implemented using a plugin. Which one do you think is best?

Leave a Reply

Your email address will not be published. Required fields are marked *