When running a magento store, you may come up with the idea of keeping your latest product(s) in the first place(s). But you still want to have the rest of the category listing ordered by another value or attribute you specified. As of Magento 1.9.2.4, there seems to be no way achieving this except for using a plugin.
If you do not want to invest into a plugin and you are are little bit into coding, than there’s another, much more easier approach to add the functionality for yourself. And since you got here, you’re probably good enough in coding to add the corresponding file by yourself.
The task
My task for a current Magento project wase:
Implement functionality that new products always come before all other products, in all store categories. For all not new products, keep the order assigned in each category by its position.
Simple as that. Well, I chose to alter the last part of the task to keep the order specified by the store manager in the backend. I figured, this way, the admin could change the order of the products to any attribute or value later on, if he wished.
The implementation
It was an intricate path, finding a solution through various blog entries, StackExchange Q&A and Magento and ZendDB documentation. Before I post the whole code, I’ll walk you through each step and explain what it is going to to.
We will be using the current template’s list.phtml
file. You should make sure that you are changing your custom theme’s file or at least create a backup of the original file. You’ll never know, for what reason you will need it again. I personally like to review the default theme’s files to get an idea of what Magento’s creators conception originally was.
Setting and retreiving the environment variables
There are a few things we need, before we get started with the final function. This is the current date, the current (or specified) sort order-field (attribute or value) and its direction (ascending or descending). So right after the developer’s notes on copyright, request these values and store them as variable.
$todayDate = Mage::app()->getLocale()->date()->toString(Varien_Date::DATETIME_INTERNAL_FORMAT); $currentOrder = $this->getChild('toolbar')->getCurrentOrder(); $currentDirection = $this->getChild('toolbar')->getCurrentDirection(); |
I was using the sorting-information from the toolbar, because it turned out that it is most reliable. There are some other ways of retreiving these information, but in most cases I only got the default value set in Magento’s system configuration. Using the toolbar’s data also respects any changes to the sort order that may be made by the frontend user (customer).
Altering the current product collection
Now it is time to change the ordering of the product collection that was retreived by Magento. It’s loaded in the very first PHP-command in the file with $_productCollection = $this->
getLoadedProductCollection();. Since we want to sort the product list by newest products before anything else, we have to reset (or remove) the current sort order from the database-request.
$_productCollection->getSelect()->reset(Zend_Db_Select::ORDER); $_productCollection->addAttributeToSort('news_to_date','DESC')->clear(); $_productCollection->getSelect()->order( $currentOrder . ' ' . $currentDirection ); |
After that, we’ll add the new attribute to sort by as first sorting criteria and clear the current product collection to re-initiate it. If you do not clear
the collection, it will show up in the frontend in the order it was originally retreived from the database.
As third step, we are adding the original product order to the current collection. With this MySQL request, the whole collection is sorted by news_to_date
in descending order first and all other products, that to not have a new-to-date stay in their original order. Descending order in news_to_date
results in those products being displayed first, whose news_to_day
is the most far away from the current day.
Double-checking the result
If you’re not quite sure that what you see is correct, you can display Magento’s current MySQL- query by adding this code somewhere before the foreach($_productCollection as $_product)
line:
echo $_productCollection->getSelect()->__toString(); |
You could copy and paste the output text from the frontend and run it thhrough you database by yourself and check, if MySQL returns the same products in the same order.
Products that ceased to be new
The code so far is working? No? Maybe there’s one thing you should have in mind: Products that have a news_to_date
in the past, are still sorted by this value. They’re not sorted by the default order.
You could now go manually into your Magento backend and edit these products by yourself. Each one, every time. But when you have multiple store editors, there’s a fair chance someone will forget to remove the outdated date. So I used the same file to manipulate the product date, if necessary.
Remember that we set the current date as variable? This is the point, where it comes in! Below the product image, I added a function to remove the newstodate when it is in the past. You could place it anywhere in the list.phtml
inside the foreach($_productCollection as $_product)
– endforeach;
loop.
But be aware of two things: It removes the date from its fiel in the database. Once it is gone, it’s gone. First of all, there’s no way to “restore” it. So if you are using past dates somewhere else in your template, you’d have to find another workaround.
Second of all, the date is removed on the first page view. So the first frontend-user that visits the page after a product ran out of being new will still have the product displayed as new. At least, when you chose so by your CSS or whatever. The date is being removed “on the fly” after the collection has been loaded.
$newToDate = $_product->getNewsToDate(); if( $newToDate < $todayDate && $newToDate !== NULL ) { $_product->setData('news_to_date', NULL)->getResource()->saveAttribute($_product, 'news_to_date'); } |
This function not only checks, if the date is in the past but also runs only, if the current date not already is NULL
.
Implementing the code
So here is the original Magento app/design/frontend/base/default/template/catalog/product/list.phtml
from the base/default-template with my code added.