Introduction

The site that I am working on had a requirement to list 5 of the Best Seller products inside a CMS page.

Magento out-the-box can inject category lists into CMS pages, but there is no built-in-way to limit the product count.

This article describes how I did this, with only using an observer / event, using the core Product List functionality. Thus no new blocks, no new templates. 

The Requirements

List, in a CMS page the top 5 best Seller products.

Listing the Best sellers is simple enough, making use of my own Dynamic Category Products module, which will allow a category to exist that is dynamically updated, daily, with the Best Selling products, counting back XX days.

Dynamic Category products Best Sellers

The Problem

The problem is that the CMS page should only list the top 5. The Dynamic Category Products can set a Limit rule, but that would then break the category view, which is supposed to list all products. Another solution would thus be required.

The default out-the-box way of inserting a category list into CMS using the {{}} tags cannot limit the product count. (yes it will limit as per the default toolbar limits, but in this instance I only want to list 5 products)

Some research reveals that all solutions involves in the creating of a new module, which contains a block which extends the core Mage_Catalog_Block_Product_List model, and then extends the _getProductCollection() method. The new module/block is then refered to from the CMS page, instead of using catalog/product_list as the block (type) to use.

Some other solutions propose to change the template to a custom template, and in the template you add some limitations to the loops. This (in my opinion) is the worst way of doing it, as the actual product collection would still load a lot more products, of which most will not be used, or displayed.  This is a waste of resources, in an already slow environemnt.

The first solution (create a new block) is not a bad solution, but it seems a bit overkill to me. Can it be done without creating a new block class? - Yes. I found a way using observer methods.

The background

The first step was to determine how magento internals get to the point of having the product collection available to the core template '/app/design/frontend/base/default/template/catalog/product/list.phtml' in the first call of '$_productCollection=$this->getLoadedProductCollection();'

Thus, using some breakpoints, and stepping back through the code form the point in the template call, you will find the following code flow path: (well the relevant parts, as there is no way to go through each step in the code flow)

Mage_Catalog_Block_Product_List::_beforeToHtml()

list.php

Here you can see that internals instantiates a toolbar object.
The method Mage_Catalog_Block_Product_List::getToolbarBlock() looks as such:

toolbar method

So, internals first checks to see if there is not already a toolbar present, by using the name of the toolbar stored in the variable ToolBarBlockName, and if not, it creates the required block. This is interesting, and vital to the solution.

Moving on through the code flow in the _beforeToHtml() method (see pointer #2), I find that the collection is set to the toolbar.

Stepping into that code, you find the following:

toolbar_set_collection

And digging in just a little further, the ->getLimit() method reveals this code:

limits

Thus, if the toolbar has a value set to _current_limit, internals will use that as the limit on the collection, and ignore the default (or lack of defau7lt) limits set to the toolbar object.

Thus it is very possible to inject a limit to the collection as it is built.

The basic steps would be :

1. Load the toolbar object
2. Store the created toolbar name in the required variable, so it can be reused later
3. Set the _current_limit in the created toolbar

Easy.

The solution 

The best place (please correct me if there is a better place/event) is via the event 'core_block_abstract_prepare_layout_before'.

This will allow me to instantiate the toolbar, save it, and inject my limit information.

The code to do this was easy, and straghtfoward.

the code

The end result is the ability to re-use core magento Catalog_Product_List block and templates, but have a product limitation.

The CMS block used to create this would look like this:

{{block type="catalog/product_list" category_id="4" product_limit="5" template="catalog/product/list.phtml"}}

I have packaged the solution into a module which is available on GitHub : https://github.com/ProxiBlue/CatalogListFilters 

Conclusion

There was thus no need to create a whole new block class, or a template to get the desired end result. The solution makes use of core code, thus less custom code, and less to maintain going forward.