Hey Everyone -
So, Tridian has been doing quite a bit of Magento development work lately, and in keeping with the spirit of “open source” software, we thought we’d share a module that we recently developed that addresses a very common need when creating an e-store with Magento. The requirements are to get the X most recently products added to the store (108 in this case) and display them in their own “New Arrivals” category, but make it so that the cart will automatically display the newest products based on the product’s creation date, without the site admin having to manually assigned them into and out of a “New Arrivals” category.
In order to create “New Arrivals” functionality on my client’s Magento store (which is 1.1.6, but this code also works for all versions of Magento through the current 1.2.0.1), I created the following module. My approach was to override the normal category behavior for my “New Arrivals” category and display a custom product collection instead.
Here’s how it went down…
Step 1: In the admin, create a category called “New Arrivals” and leave it empty.
Step 2: Find the code that generates the product listings on the Category pages. Turns out the code that does this is in the class Mage_Catalog_Block_Product_List. Great. But now, in order to get the cart to do what we want, we have to override this class and add my own custom code to handle the creation of the product collection for the empty category “New Arrivals”. To do this, I created a new block file called Tridian_Newarrivals_Block_Newarrivals, which extends the original, and saved it here: app/code/local/Tridian/Newarrivals/Block/Newarrivals.php.
class Tridian_Newarrivals_Block_Newarrivals extends Mage_Catalog_Block_Product_List { protected function _getProductCollection() { if (is_null($this->_productCollection)) { $curr_category = Mage::registry('current_category')->getName(); $curr_categoryid = Mage::registry('current_category')->getId(); $curr_category = strtolower($curr_category); if($curr_category == 'new arrivals') { $storeId = Mage::app()->getStore()->getId(); $product = Mage::getModel('catalog/product'); $this->_productCollection = Mage::getModel('catalog/resource_eav_mysql4_product_collection') ->setStoreId($storeId) ->addAttributeToSelect(array('name', 'price', 'small_image'), 'inner') ->addAttributeToSelect(array('special_price'), 'left') ->addAttributeToSelect('status') ->setOrder('created_at', 'desc') ->setPageSize(12) ->addUrlRewrite($curr_categoryid) ;//->load(true); // passing true to load will echo the sql to the browser. Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($this->_productCollection); // add InStock filter to prevent out of stock stuff from showing up. Chris Lohman 1/29/2009 Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($this->_productCollection); Mage::getSingleton('catalog/product_visibility')->addVisibleInSearchFilterToCollection($this->_productCollection); } else { $this->_productCollection = parent::_getProductCollection(); } } return $this->_productCollection; } }
As you can see in the code above, when the category name equals “new arrivals”, we implement the custom product collection. If not, then the class calls the parent function, which is just the _getProductCollection() function in the Mage_Catalog_Block_Product_List class. The key things to notice here are the page size (12), which should be chosen to match your default page size for your store (our client’s store defaults to 12 items per page), and the setOrder(’created_at’, ‘desc’) call, which sorts the results in descending order by when the product was created/added to the Magento admin. You can also see that we’ve added some filters to the collection to keep unwanted products from showing up in the results (out of stock items, etc).
Great! Sounds like we’re done, right? Not so fast. Turns out that the toolbar that handles the category pagination uses the size of the product collection (which in this case is the entire catalog, sorted from most recently added to least recently added). Doh. We only want to show the most recent 108. In order for our result set to actually be limited to 108 products, it turns out that we also need to change the behavior of the Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection class to use our new limit instead of using the size of the returned product collection. That leads us to…
Step 3: Create the following model class: app/code/local/Tridian/Newarrivals/Model/Newarrivals.php
class Tridian_Newarrivals_Model_Newarrivals extends Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection { /** * Render SQL for retrieve product count */ public function getSelectCountSql() { $curr_category = Mage::registry('current_category')->getName(); $curr_category = strtolower($curr_category); if($curr_category == 'new arrivals') { $sql = "select 108"; } else { $sql = parent::getSelectCountSql(); } return $sql; } }
You’re probably wondering why I picked 108, right? Well to make a long story short - my catalog pages display either 12, 24, or 36 items. If I didn’t use a multiple of all of those values, then the toolbar would display the pagination links incorrectly. If you don’t believe me, try it out for yourself. As before, you see that if the category isn’t “new arrivals”, then the class falls back to its original behavior by calling the parent function.
Well done! The hard part is over. Now all we need to do is activate our module in Magento and then set up our rewrite xml in our config.xml file to get Magento to use our new classes instead of the originals.
Here’s the code to tell the cart to override the original block and model classes. Save it as config.xml and put it here app/code/local/Tridian/Newarrivals/etc/config.xml:
<?xml version="1.0"?> <config> <modules> <Tridian_Newarrivals> <version>0.1.0</version> </Tridian_Newarrivals> </modules> <global> <blocks> <catalog> <rewrite> <product_list>Tridian_Newarrivals_Block_Newarrivals</product_list> </rewrite> </catalog> </blocks> <models> <catalog> <rewrite> <resource_eav_mysql4_product_collection>Tridian_Newarrivals_Model_Newarrivals</resource_eav_mysql4_product_collection> </rewrite> </catalog> </models> </global> </config>
You need to activate your module in app/etc/modules. We created a file named Tridian_All.xml for this.
<?xml version="1.0"?> <config> <modules> <Tridian_Newarrivals> <active>true</active> <codePool>local</codePool> </Tridian_Newarrivals> </modules> </config>
Just to be crystal clear, since this is where a lot of slip ups occur with Magento – make sure you follow the naming conventions and file locations to a “T”. They are very specific and case sensitive.
As a recap, here’s where your files should go:
Tridian_All.xml (turns on module) goes -> app/etc/modules/Tridian_All.xml
Module code (3 components) goes -> app/code/local/Tridian/Newarrivals
Block class Tridian_Newarrivals_Block_Newarrivals goes -> app/code/local/Tridian/Newarrivals/Block/Newarrivals.php
Model class Tridian_Newarrivals_Model_Newarrivals goes -> app/code/local/Tridian/Newarrivals/Model/Newarrivals.php
config.xml (xml config file for module) goes -> app/code/local/Tridian/Newarrivals/etc/config.xml
And there you have it. The front end will now display your most recent products in their own “New Arrivals” category and it will always be the most recently added.
Feel free to post any questions or comments.
Cheers!
Posted by: chrisTags: development, ecommerce, Magento










Hi Chris,
Just implemented your module, but I don’t really get the part where I have to disable stuff. Resulting in just seeing code on my local install when I click on a menu item.
I have 1.3 installed, could you please point me in the right way ?
Kind regards,
Jeroen
Hey Jeroen,
I’m happy to help, but I need to know a bit more about where your problem is. What are you disabling? I just reread my post and I didn’t see anywhere where I specified disabling anything. Also, what is the code that you are seeing when you click on a menu item?
Chris
Hi Chris,
Thanks for your reply! I’ve reread your post to and I misread is apparently , sorry about that. The code I see on the screen is the following.
class Tridian_Newarrivals_Block_Newarrivals extends Mage_Catalog_Block_Product_List { protected function _getProductCollection() { if (is_null($this->_productCollection)) { $curr_category = Mage::registry(’current_category’)->getName(); $curr_categoryid = Mage::registry(’current_category’)->getId(); $curr_category = strtolower($curr_category); if($curr_category == ‘new arrivals’) { $storeId = Mage::app()->getStore()->getId(); $product = Mage::getModel(’catalog/product’); $this->_productCollection = Mage::getModel(’catalog/resource_eav_mysql4_product_collection’) ->setStoreId($storeId) ->addAttributeToSelect(array(’name’, ‘price’, ’small_image’), ‘inner’) ->addAttributeToSelect(array(’special_price’), ‘left’) ->addAttributeToSelect(’status’) ->setOrder(’created_at’, ‘desc’) ->setPageSize(12) ->addUrlRewrite($curr_categoryid) ;//->load(true); // passing true to load will echo the sql to the browser. Mage::getSingleton(’catalog/product_status’)->addVisibleFilterToCollection($this->_productCollection); // add InStock filter to prevent out of stock stuff from showing up. Chris Lohman 1/29/2009 Mage::getSingleton(’cataloginventory/stock’)->addInStockFilterToCollection($this->_productCollection); Mage::getSingleton(’catalog/product_visibility’)->addVisibleInSearchFilterToCollection($this->_productCollection); } else { $this->_productCollection = parent::_getProductCollection(); } } return $this->_productCollection; } }class Tridian_Newarrivals_Block_Newarrivals extends Mage_Catalog_Block_Product_List { protected function _getProductCollection() { if (is_null($this->_productCollection)) { $curr_category = Mage::registry(’current_category’)->getName(); $curr_categoryid = Mage::registry(’current_category’)->getId(); $curr_category = strtolower($curr_category); if($curr_category == ‘new arrivals’) { $storeId = Mage::app()->getStore()->getId(); $product = Mage::getModel(’catalog/product’); $this->_productCollection = Mage::getModel(’catalog/resource_eav_mysql4_product_collection’) ->setStoreId($storeId) ->addAttributeToSelect(array(’name’, ‘price’, ’small_image’), ‘inner’) ->addAttributeToSelect(array(’special_price’), ‘left’) ->addAttributeToSelect(’status’) ->setOrder(’created_at’, ‘desc’) ->setPageSize(12) ->addUrlRewrite($curr_categoryid) ;//->load(true); // passing true to load will echo the sql to the browser. Mage::getSingleton(’catalog/product_status’)->addVisibleFilterToCollection($this->_productCollection); // add InStock filter to prevent out of stock stuff from showing up. Chris Lohman 1/29/2009 Mage::getSingleton(’cataloginventory/stock’)->addInStockFilterToCollection($this->_productCollection); Mage::getSingleton(’catalog/product_visibility’)->addVisibleInSearchFilterToCollection($this->_productCollection); } else { $this->_productCollection = parent::_getProductCollection(); } } return $this->_productCollection; } }
Thanks again,
Jeroen
OMG!
I just found out what I did wrong and it’s really embarrassing! I forgot to wrap the classes in php tags
Sorry for wasting your time! It works perfectly!
Regards,
Jeroen
Hi chris,
this is a nice tutorial for making a new module and override magento’s built in modules, but it’s not final as i understand.
1. There are still issues with pagination. (when viewing Catalog ‘New Arrivals’, i get 108 items count of new objects, even if i have like 10 products in my inventory).
2. I can’t see the product-list from this category within admin’s Manage Catalogs submenu (where on the left there is the list of Catalogs and the object count for each catalog entry)
THe first thing is what i would like to solve as the second one i guess is a additional feature.
Thanks.
Hey deXX,
Thanks for the comment. Regarding your 2 issues…
Let’s deal with #1 first - Did you see my explanation on why I picked that number (108) and why you should pick yours according to your catalog? If you have 10 items in your product catalog, I would suggest changing the following code:
to
As your product catalog grows from 10 (maybe it won’t), you should consider changing this if you want to feature more products in the new arrivals category. Cool?
Now to #2… What you are talking about here isn’t what I was shooting for with this module. I’m not even sure it’s possible? In my scenario, the client has 1000’s of products that are constantly being added to, so the amount of work to keep a real category fresh is significant if it’s not automated. The second thing is that the products that are assigned to my “new-arrivals” category are determined dynamically everytime the page is loaded, instead of being defined by relational data in the database. I don’t know if you’ve looked under the hood at how products are assigned to categories, but I did, and I decided not to go that route here. In order to do that, I think you would have to set up a cron job to run a script periodically to create those category associations (to the latest products). That’s a different but equally effective approach, I think, and I considered that option when I created the module, but ultimately I thought my way was easier, and that’s why I did it that way. The only way to get products to show up in the Manage Categories section for new arrivals is to define those relationships in the database, and for that you’d have to take the cron job approach, or implement an Observer class to republish the products and their categories.
Hello chris thanks for this module, is this working on the new magento version im using 1.3.1 and tested i did every step u said above but still products are not showing on my new arrivals category “There are no products matching the selection.”? any idea? or am i missing something?
thanks
Daniel
No errors or anything show up? I haven’t tested this module with 1.3.1, but my first question is did you make sure that you overrode the base 1.3.1 classes and not what I’m using above? Sometimes when you move from version to version of magento, some embedded code in your functions needs to be updated if the inherited class changes significantly. I have a test version of 1.3.0 installed, so let me try it on that and get back to you.
Looks like it works on 1.3.0 without any mods. Check it out:
http://dev5.tridian.com/magentostore/magento/index.php/new-arrivals.html
Would you send me a link to you store so I can see?
Hey Chris thanks yeah it works now sorry my wrong i forgot to clear cache thats the only problem
the store is http://wheredyougetthatfrom.com/new-arrivals.html its using 1.3.1 fresh install
thanks again for this great contribution, sorry to disturb u
GODbless!!
Daniel
HELP! I love the idea of this module. It is working perfectly on my category pages. But it is giving an error and blowing up my homepage.
I am using the following code on my homepage to display a specific category of items.
{{block type=”catalog/product_list” category_id=”5″ template=”catalog/product/list.phtml”}}
When I use your module as detailed above, I get the following error message on my hompage: “Fatal error: Call to a member function getId() on a non-object in app/code/local/Tridian/Newarrivals/Block/Newarrivals.php on line 9″
If I remove the homepage category code, the homepage displays fine.
Any help? I’m thinking that there needs to be some conditional statement to prevent the script from running on non-category pages?? But I’m not a programmer so I dont know all the proper syntax to try it.
Try the following in class Tridian_Newarrivals_Block_Newarrivals (app/code/local/Tridian/Newarrivals/Block/Newarrivals)… Move the getId() line inside the if statement directly below it. Like so
Old:
$curr_category = Mage::registry(’current_category’)->getName();
$curr_categoryid = Mage::registry(’current_category’)->getId();
$curr_category = strtolower($curr_category);
if($curr_category == ‘new arrivals’)
{
…
New:
$curr_category = Mage::registry(’current_category’)->getName();
$curr_category = strtolower($curr_category);
if($curr_category == ‘new arrivals’)
{
$curr_categoryid = Mage::registry(’current_category’)->getId();
…
I think that should do it. Save your file and upload the changes to your server. Let me know how it goes!
Hi Chris,
It’s great extension, I test it in 1.3.2.4, it works without problem.
However, I try to show new arrivals in home page, I tried to use below code:
{{block type=”catalog/product_list” category_id=”35″ template=”catalog/product/list.phtml”}}
I got error:
Fatal error: Call to a member function getName() on a non-object in D:\xampp\htdocs\se55\app\code\local\Tridian\Newarrivals\Block\Newarrivals.php on line 9
I have change code as you suggested, but the problem still exist, I am not a programmer, can you help me?
Regards,
Didn’t work for me. I’m still getting the error. Help?
Hello,
This was exactly what I was looking for, however it hasn’t worked yet for me. I get a message “There are no products matching the selection” when I navigate to the “New Arrivals” category. I’m currently using magento version 1.3.1. Any thoughts?
Also - it seems that “New Arrivals” doesn’t show up in my nav bar on my homepage. However, it re-appears in my nav bar when I click to another category??
Thanks for any help,
Erik
Hey Erik,
Did you make sure to create the empty category in the categories admin? If you do that, and make sure it’s active, it should show up in the site nav. I tested this module in 1.3 and it worked, but that was before 1.3.1 I believe. In order to figure out the No products issue, you should modify your collection statement by uncommenting the ->load(true) portion to output the sql that’s generated and then run that in your db tool to see if there’s an issue. Hopefully that will point you in the right direction.
-Chris
Hey Chris, thanks for the code. I’ve got this working quite nicely so far except for a small bug with pagination. Basically clicking on the “show all” link returns the ENTIRE catalog. I’m not sure how magento is bypassing the overridden sql query class there but there is a chance that some other class has to be overridden somewhere.
A couple of tips for people having problems. First test to see that your module is working correctly. If it is, and you are still getting no results, make sure that the items you are adding to the querry like: ->addAttributeToSelect(array(’name’, ‘price’, ‘image’), ‘inner’) actualy exist.
For example, I had ->addAttributeToSelect(array(’name’, ‘price’, ‘image’, ’small_image’), ‘inner’),
but I had no small image on my products, so nothign was showing up. I would try first to query without adding extra attributes to select and see if that works.
Hey Trent,
Did you implement step 3 in my example? That’s where the total number of records is overridden in Magento. Prior to implementing that piece, I was getting all 1500 records returned to me.
Cheers,
Chris
Yes I did for sure, and it is working because I debugged it and it is getting to that code. When you use regular pages, it does only querry for 32. However when you click all it goes for the whole catalog.
resource_eav_mysql4_product_collection Also seems to have a problem in 1.3.1 and up as I can’t get it working on one of my newer magento installs. Any ideas?
The only idea I can think of is to try to echo the sql statement within the product collection and see it’s behavior as you pick the different options in the show X per page dropdown. Perhaps it’s going to another class for the “all” option? I just spent about 20 minutes looking for where the “X per page” value is updated in the collection query, and I haven’t had any luck. But that’s probably the missing link.
Sound great. My requirement is similar. I need to display last three recent added products to the right of every page. Any help?
Hi Gopi, I would suggest implementing something like this instead: http://inchoo.net/ecommerce/magento/featured-products-on-magento-frontpage/. My module is more appropriate as a category instead of a block section on several pages. Good Luck!
I think it is great. But let me know how it goes.
Hi Chris
i have tried those scripts and my site has gone wrong. can you please help me
thanks
i have checked all the codes and location i am not sure what i have done wrong
I’m having the same problem as S above. Moving the line of code into the if statement didn’t fix it. Any ideas?
Thanks. Worked a treat on version 1.3.2.4
Also, this should be added to NewArrivals.php if viewing products in list mode:
->addAttributeToSelect(’short_description’)
Hey Chris,
This code is working superb but i am not able to success of sorting specially sort by price. will you please guide?
Thanks,