Some clients have a need to show product on sale on homepage, or, sometimes, to filter products on sale in each category individually. In this post i’ll show you two ways of listing products on sale:
- On CMS page as a list
- By using a filter in category view
Let’s get to it!
Static CMS page
This is just a quick way of showing products on sale on one page, using admin and uploading one phtml file. Going down this path, you won’t be able to use pagination nor layered navigation. In case you would like those features, I’d recommend following the second part (Filter in Category) of this post.
Create a new CMS page with content set to
{{block type="catalog/product_list" template="inchoo/onsale/sale.phtml"}}
and a template for listing your products.
app/design/frontend/default/YOUR_THEME/template/inchoo/onsale/sale.phtml
<?php
$_productCollection = Mage::getModel('catalog/product')->getCollection();
$_productCollection->addAttributeToSelect(array(
'image',
'name',
'short_description'
))
->addFieldToFilter('visibility', array(
Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH,
Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_CATALOG
)) //showing just products visible in catalog or both search and catalog
->addFinalPrice()
// ->addAttributeToSort('price', 'asc') //in case we would like to sort products by price
->getSelect()
->where('price_index.final_price < price_index.price')
// ->limit(30) //we can specify how many products we want to show on this page
// ->order(new Zend_Db_Expr('RAND()')) //in case we would like to sort products randomly
;
Mage::getModel('review/review')->appendSummary($_productCollection);
$_helper = $this->helper('catalog/output');
?>
//
<?php if(!$_productCollection->count()): ?>
<p class="note-msg"><?php echo $this->__('There are no products matching the selection.') ?></p>
<?php else: ?>
<div class="category-products">
<?php // List mode ?>
<?php $_iterator = 0; ?>
<ol class="products-list" id="products-list">
<?php foreach ($_productCollection as $_product): ?>
<li class="item<?php if( ++$_iterator == sizeof($_productCollection) ): ?> last<?php endif; ?>">
<?php // Product Image ?>
<a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->stripTags($this->getImageLabel($_product, 'small_image'), null, true) ?>" class="product-image"><img src="<?php echo $_product->getImageUrl(); ?>" width="135" height="135" alt="<?php echo $this->stripTags($this->getImageLabel($_product, 'small_image'), null, true) ?>" /></a>
<?php // Product description ?>
<div class="product-shop">
<div class="f-fix">
<?php $_productNameStripped = $this->stripTags($_product->getName(), null, true); ?>
<h2 class="product-name"><a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $_productNameStripped; ?>"><?php echo $_helper->productAttribute($_product, $_product->getName() , 'name'); ?></a></h2>
<?php if($_product->getRatingSummary()): ?>
<?php echo $this->getReviewsSummaryHtml($_product) ?>
<?php endif; ?>
<?php echo $this->getPriceHtml($_product, true) ?>
<?php if($_product->isSaleable()): ?>
<p><button type="button" title="<?php echo $this->__('Add to Cart') ?>" class="button btn-cart" onclick="setLocation('<?php echo $this->getAddToCartUrl($_product) ?>')"><span><span><?php echo $this->__('Add to Cart') ?></span></span></button></p>
<?php else: ?>
<p class="availability out-of-stock"><span><?php echo $this->__('Out of stock') ?></span></p>
<?php endif; ?>
<div class="desc std">
<?php echo $_helper->productAttribute($_product, $_product->getShortDescription(), 'short_description') ?>
<a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $_productNameStripped ?>" class="link-learn"><?php echo $this->__('Learn More') ?></a>
</div>
<ul class="add-to-links" style="margin:0; padding-left:0; list-style: none;">
<?php if ($this->helper('wishlist')->isAllow()) : ?>
<li><a href="<?php echo $this->helper('wishlist')->getAddUrl($_product) ?>" class="link-wishlist"><?php echo $this->__('Add to Wishlist') ?></a></li>
<?php endif; ?>
<?php if($_compareUrl=$this->getAddToCompareUrl($_product)): ?>
<li><span class="separator">|</span> <a href="<?php echo $_compareUrl ?>" class="link-compare"><?php echo $this->__('Add to Compare') ?></a></li>
<?php endif; ?>
</ul>
</div>
</div>
</li>
<?php endforeach; ?>
</ol>
<script type="text/javascript">decorateList('products-list', 'none-recursive')</script>
</div>
<?php endif; ?>
That’s it! Try and visit your CMS page. You’ll see that the products with special price are listed in a ‘list view’, something like this:
With small modifications, you could display products in ‘grid view’ as well.
Filter in Category View
Magento applies filtering and loads collection to layered navigation before that same collection gets loaded in the template. That’s why we need to overwrite _getProductCollection method, so that we can apply custom filtering before the collection gets loaded. Let’s do that by extending Mage_Catalog_Product_List class and rendering our block instead of the default one on category view.
Let’s register out little module. For our purposes, let’s name it ‘Inchoo/Onsale’.
app/etc/modules/Inchoo_Onsale.xml
<?xml version="1.0"?>
<config>
<modules>
<Inchoo_Onsale>
<active>true</active>
<codePool>local</codePool>
</Inchoo_Onsale>
</modules>
</config>
Let’s also configure our module. We’ll use a block, helper, and modify the layout of our template a little bit.
app/code/local/Inchoo/Onsale/etc/config.xml
<?xml version="1.0"?>
<config>
<modules>
<Inchoo_Onsale>
<version>1.0.0.3</version>
</Inchoo_Onsale>
</modules>
<global>
<blocks>
<inchoo_onsale>
<class>Inchoo_Onsale_Block</class>
</inchoo_onsale>
</blocks>
<helpers>
<inchoo_onsale>
<class>Inchoo_Onsale_Helper</class>
</inchoo_onsale>
</helpers>
</global>
<frontend>
<layout>
<updates>
<onsale>
<file>inchoo/onsale.xml</file>
</onsale>
</updates>
</layout>
</frontend>
</config>
Now, register a helper that will have two methods: getOnSaleUrl and getNotOnSaleUrl. They’ll be used by our checkbox, to fetch URL that will be loaded upon click. We’ll also add a new variable to URL – ‘sale‘. When sale equals 1, we’ll show products on sale, if it is not set, we’ll show all products in category.
app/code/local/Inchoo/Onsale/Helper/Data.php
<?php
class Inchoo_Onsale_Helper_Data extends Mage_Core_Helper_Abstract
{
public function getOnSaleUrl()
{
$url = Mage::getUrl('', array(
'_current' => true,
'_use_rewrite' => true,
'_query' => array(
'sale' => 1,
'p' => NULL
)
));
return $url;
}
public function getNotOnSaleUrl()
{
$url = Mage::getUrl('', array(
'_current' => true,
'_use_rewrite' => true,
'_query' => array(
'sale' => NULL,
'p' => NULL
)
));
return $url;
}
}
Now, lets create our block. We’ll extend our class from Mage_Catalog_Block_Product_List, and write our own _getProductCollection method.
app/code/local/Inchoo/Onsale/Block/Catalog/Product/List.php
<?php
class Inchoo_Onsale_Block_Catalog_Product_List extends Mage_Catalog_Block_Product_List
{
protected function _getProductCollection()
{
if (is_null($this->_productCollection)) {
$layer = $this->getLayer();
/* @var $layer Mage_Catalog_Model_Layer */
if ($this->getShowRootCategory()) {
$this->setCategoryId(Mage::app()->getStore()->getRootCategoryId());
}
// if this is a product view page
if (Mage::registry('product')) {
// get collection of categories this product is associated with
$categories = Mage::registry('product')->getCategoryCollection()
->setPage(1, 1)
->load();
// if the product is associated with any category
if ($categories->count()) {
// show products from this category
$this->setCategoryId(current($categories->getIterator()));
}
}
$origCategory = null;
if ($this->getCategoryId()) {
$category = Mage::getModel('catalog/category')->load($this->getCategoryId());
if ($category->getId()) {
$origCategory = $layer->getCurrentCategory();
$layer->setCurrentCategory($category);
$this->addModelTags($category);
}
}
$this->_productCollection = $layer->getProductCollection();
// inchoo start
$param = Mage::app()->getRequest()->getParam('sale');
if(isset($param) && $param==='1'){
$this->_productCollection
->addFinalPrice()
->getSelect()
->where('price_index.final_price < price_index.price');
}
// inchoo end
$this->prepareSortableFieldsByCategory($layer->getCurrentCategory());
if ($origCategory) {
$layer->setCurrentCategory($origCategory);
}
}
return $this->_productCollection;
// return parent::_getProductCollection();
}
}
We’ll also add a checkbox to our toolbar, where our customer will be able to filter products that are on sale. Open your toolbar.phtml file.
app/design/frontend/default/YOUR_THEME/template/inchoo/onsale/toolbar.phtml
<!--inchoo start-->
<?php $helper = Mage::helper('inchoo_onsale'); ?>
<!--inchoo end-->
<?php if($this->getCollection()->getSize()): ?>
<div class="toolbar">
<div class="pager">
<p class="amount">
<?php if($this->getLastPageNum()>1): ?>
<?php echo $this->__('Items %s to %s of %s total', $this->getFirstNum(), $this->getLastNum(), $this->getTotalNum()) ?>
<?php else: ?>
<strong><?php echo $this->__('%s Item(s)', $this->getTotalNum()) ?></strong>
<?php endif; ?>
</p>
<div class="limiter">
<label><?php echo $this->__('Show') ?></label>
<select onchange="setLocation(this.value)">
<?php foreach ($this->getAvailableLimit() as $_key=>$_limit): ?>
<option value="<?php echo $this->getLimitUrl($_key) ?>"<?php if($this->isLimitCurrent($_key)): ?> selected="selected"<?php endif ?>>
<?php echo $_limit ?>
</option>
<?php endforeach; ?>
</select> <?php echo $this->__('per page') ?>
</div>
<?php echo $this->getPagerHtml() ?>
</div>
<?php if( $this->isExpanded() ): ?>
<div class="sorter">
<?php if( $this->isEnabledViewSwitcher() ): ?>
<p class="view-mode">
<?php $_modes = $this->getModes(); ?>
<?php if($_modes && count($_modes)>1): ?>
<label><?php echo $this->__('View as') ?>:</label>
<?php foreach ($this->getModes() as $_code=>$_label): ?>
<?php if($this->isModeActive($_code)): ?>
<strong title="<?php echo $_label ?>" class="<?php echo strtolower($_code); ?>"><?php echo $_label ?></strong>
<?php else: ?>
<a href="<?php echo $this->getModeUrl($_code) ?>" title="<?php echo $_label ?>" class="<?php echo strtolower($_code); ?>"><?php echo $_label ?></a>
<?php endif; ?>
<?php endforeach; ?>
<?php endif; ?>
</p>
<?php endif; ?>
<div class="sort-by">
<label><?php echo $this->__('Sort By') ?></label>
<select onchange="setLocation(this.value)">
<?php foreach($this->getAvailableOrders() as $_key=>$_order): ?>
<option value="<?php echo $this->getOrderUrl($_key, 'asc') ?>"<?php if($this->isOrderCurrent($_key)): ?> selected="selected"<?php endif; ?>>
<?php echo $this->__($_order) ?>
</option>
<?php endforeach; ?>
</select>
<?php if($this->getCurrentDirection() == 'desc'): ?>
<a href="<?php echo $this->getOrderUrl(null, 'asc') ?>" title="<?php echo $this->__('Set Ascending Direction') ?>"><img src="<?php echo $this->getSkinUrl('images/i_desc_arrow.gif') ?>" alt="<?php echo $this->__('Set Ascending Direction') ?>" class="v-middle" /></a>
<?php else: ?>
<a href="<?php echo $this->getOrderUrl(null, 'desc') ?>" title="<?php echo $this->__('Set Descending Direction') ?>"><img src="<?php echo $this->getSkinUrl('images/i_asc_arrow.gif') ?>" alt="<?php echo $this->__('Set Descending Direction') ?>" class="v-middle" /></a>
<!--inchoo start-->
<label for="sale"><?php echo $this->__('On Sale') ?></label>
<input name="sale" id="sale" type="checkbox"
value="<?php
$currentUrl = Mage::helper('core/url')->getCurrentUrl();
if($currentUrl===$helper->getOnSaleUrl()):
echo $helper->getNotOnSaleUrl();
echo ""checked="checked";
else:
echo $helper->getOnSaleUrl();
endif;
?>" autocomplete="off" onchange="setLocation(this.value)"/>
<!--inchoo end-->
<?php endif; ?>
</div>
</div>
<?php endif; ?>
</div>
<?php endif ?>
And, lastly, create our layout file to make Magento use our toolbar.phtml template instead of the default one.
app/design/frontend/default/YOUR_THEME/layout/inchoo/onsale.xml
<?xml version="1.0"?>
<layout version="0.1.0">
<catalog_category_default>
<reference name="category.products">
<block type="inchoo_onsale/catalog_product_list" name="product_list" template="catalog/product/list.phtml">
<block type="catalog/product_list_toolbar" name="product_list_toolbar" template="inchoo/onsale/toolbar.phtml">
<block type="page/html_pager" name="product_list_toolbar_pager"/>
<!-- The following code shows how to set your own pager increments -->
<!--
<action method="setDefaultListPerPage"><limit>4</limit></action>
<action method="setDefaultGridPerPage"><limit>9</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>2</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>4</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>6</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>8</limit></action>
<action method="addPagerLimit" translate="label"><mode>list</mode><limit>all</limit><label>All</label></action>
-->
</block>
<action method="addColumnCountLayoutDepend"><layout>empty</layout><count>6</count></action>
<action method="addColumnCountLayoutDepend"><layout>one_column</layout><count>5</count></action>
<action method="addColumnCountLayoutDepend"><layout>two_columns_left</layout><count>4</count></action>
<action method="addColumnCountLayoutDepend"><layout>two_columns_right</layout><count>4</count></action>
<action method="addColumnCountLayoutDepend"><layout>three_columns</layout><count>3</count></action>
<action method="setToolbarBlockName"><name>product_list_toolbar</name></action>
</block>
</reference>
</catalog_category_default>
<catalog_category_layered>
<reference name="category.products">
<block type="inchoo_onsale/catalog_product_list" name="product_list" template="catalog/product/list.phtml">
<block type="catalog/product_list_toolbar" name="product_list_toolbar" template="inchoo/onsale/toolbar.phtml">
<block type="page/html_pager" name="product_list_toolbar_pager"/>
<!-- The following code shows how to set your own pager increments -->
<!--
<action method="setDefaultListPerPage"><limit>4</limit></action>
<action method="setDefaultGridPerPage"><limit>9</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>2</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>4</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>6</limit></action>
<action method="addPagerLimit"><mode>list</mode><limit>8</limit></action>
<action method="addPagerLimit" translate="label"><mode>list</mode><limit>all</limit><label>All</label></action>
-->
</block>
<action method="addColumnCountLayoutDepend"><layout>empty</layout><count>6</count></action>
<action method="addColumnCountLayoutDepend"><layout>one_column</layout><count>5</count></action>
<action method="addColumnCountLayoutDepend"><layout>two_columns_left</layout><count>4</count></action>
<action method="addColumnCountLayoutDepend"><layout>two_columns_right</layout><count>4</count></action>
<action method="addColumnCountLayoutDepend"><layout>three_columns</layout><count>3</count></action>
<action method="setToolbarBlockName"><name>product_list_toolbar</name></action>
</block>
</reference>
</catalog_category_layered>
</layout>
Note that layered navigation works, as well as pagination. Just what we wanted.
P.S. You can always view this module on GitHub or download zipped file and install it.