In one of upcoming projects I have something similar to implement. So spent some time to come up with a quick and dirty solution.
Please note that it's not a proper/complete solution. Core files have been changed along with 3rd party extension files.
Chances are it's not going to make sense in first iteration. But probaly it can reduce the development time.
Pre-requisite: Some knowledge of how Layered navigation works in Magento is required.
Summary of changes:
app/code/core/Mage/Catalog/Model/Layer/Filter/Abstract.php
/**
* Get all filter items
*
* @return array
*/
public function getItems()
{
if (empty($this->_items)) {
$this->_initItems();
}
return $this->_items;
}
app/code/core/Mage/Catalog/Model/Resource/Layer/Filter/Price.php
A bunch of methods have been added to support multi-filter price behaviour. These methods have been copied from the core class Mage_Catalog_Model_Layer_Filter_Price
and it's parent classes. It will take some effort to study the code below and link it together:
/**
* Apply price range filter to product collection
*
* @param Mage_Catalog_Model_Layer_Filter_Price $filter
* @return Mage_Catalog_Model_Resource_Layer_Filter_Price
*/
public function applyPriceRangeMulti($filter)
{
$reqParams = Mage::app()->getRequest()->getParam($filter->getRequestVar());
//validate filter
$filterParams = array_unique(explode(',', $reqParams));
$formattedParams = array();
foreach($filterParams as $param){
$filterParam = $this->_validatePriceFilter($param);
if (!$filterParam) {
return $this;
}
list($from, $to) = $filterParam;
if ($from < 0.01 && $to < 0.01) {
return $this;
}
$formattedParams[] = $filterParam;
}
$select = $filter->getLayer()->getProductCollection()->getSelect();
$priceExpr = $this->_getPriceExpression($filter, $select, false);
$andClauses = array();
foreach($formattedParams as $filterParam){
list($from, $to) = $filterParam;
/*
* Workaround for JS
*/
if ($to == 0) {
$to = '';
}
$filter->setInterval(array($from, $to));
$index = array_search("{$from}-{$to}",$filterParams);
$itemValue = $filterParams;
if (false === $index){
$itemValue[] = "{$from}-{$to}";
}else {
$itemValue[$index] = null;
unset($itemValue[$index]);
}
$itemValue = implode(',', $itemValue);
$query = array(
$filter->getRequestVar() => $itemValue,
Mage::getBlockSingleton('page/html_pager')->getPageVarName() => null // exclude current page from urls
);
$url = Mage::helper('amshopby/url')->getFullUrl($query);
$text .= $this->_renderRangeLabel($from, $to) . " ";
$children[] = array(
'label' => $this->_renderRangeLabel($from, $to),
'url' => $url,
);
if ($to !== '') {
$to = (float)$to;
if ($from == $to) {
$to += self::MIN_POSSIBLE_PRICE;
}
}
if ($from !== '') {
//$select->where($priceExpr . ' >= ' . $this->_getComparingValue($from, $filter));
$fromExpr = new Zend_Db_Expr($priceExpr . ' >= ' . $this->_getComparingValue($from, $filter));
$andString .= $fromExpr;
}
if ($to !== '') {
//$select->where($priceExpr . ' < ' . $this->_getComparingValue($to, $filter));
$toExpr = new Zend_Db_Expr($priceExpr . ' < ' . $this->_getComparingValue($to, $filter));
}
$andClauses[] = "{$fromExpr} AND {$toExpr}";
}
foreach($andClauses as $andClause){
$select->orWhere($andClause);
}
$state = $this->_createItem($filter, $text, $filterParams)
->setVar($filter->getRequestVar());
if (count($children) > 1) {
$state->setData('children', $children);
}
$filter->getLayer()->getState()->addFilter($state);
return $this;
}
/**
* Prepare text of range label
*
* @param float|string $fromPrice
* @param float|string $toPrice
* @return string
*/
protected function _renderRangeLabel($fromPrice, $toPrice)
{
$store = Mage::app()->getStore();
$formattedFromPrice = $store->formatPrice($fromPrice);
if ($toPrice === '') {
return Mage::helper('catalog')->__('%s and above', $formattedFromPrice);
} elseif ($fromPrice == $toPrice && Mage::app()->getStore()->getConfig(self::XML_PATH_ONE_PRICE_INTERVAL)) {
return $formattedFromPrice;
} else {
if ($fromPrice != $toPrice) {
$toPrice -= .01;
}
return Mage::helper('catalog')->__('%s - %s', $formattedFromPrice, $store->formatPrice($toPrice));
}
}
protected function _createItem($filter, $label, $value, $count=0)
{
return Mage::getModel('catalog/layer_filter_item')
->setFilter($filter)
->setLabel($label)
->setValue($value)
->setCount($count);
}
/**
* Validate and parse filter request param
*
* @param string $filter
* @return array|bool
*/
protected function _validatePriceFilter($filter)
{
$filter = explode('-', $filter);
if (count($filter) != 2) {
return false;
}
foreach ($filter as $v) {
if (($v !== '' && $v !== '0' && (float)$v <= 0) || is_infinite((float)$v)) {
return false;
}
}
return $filter;
}
app/code/local/Package/SomeExtension/Model/Catalog/Layer/Filter/Price/Price17ce.php
This is an extension specific file that overrided the apply() method from parent class Mage_Catalog_Model_Layer_Filter_Price
. In most cases this parent class will require modifications. To explain a bit _applyPriceRange()
method has been changed to _applyPriceRangeMulti()
. Definition of this method has been pasted above already. Again it's a copy-paste from the _applyPriceRange()
for the most part.
protected function _applyPriceRangeMulti()
{
$this->_getResource()->applyPriceRangeMulti($this);
}
Please put a comment if any one needs any more explanation.