In this tutorial I show how in easy way create the module that will allow user to browse products by attribute options and categories. This will be done using custom controller and attributes passed by URL which will make our site more SEO friendly.
Let’s create module directories:
- app/code/local/MageDev/Filter – location of filter module files
- app/code/local/MageDev/Filter/etc – config data
- app/code/local/MageDev/Filter/controllers – custom controllers
- app/code/local/Block – here goes blocks logic
- app/code/local/Helper – general functionality
Configuration data goes to config.xml file inside etc directory:
<?xml version="1.0"?> <config> <modules> <MageDev_Filter> <version>0.0.1</version> </MageDev_Filter> </modules> <global> <blocks> <MageDev_Filter> <class>MageDev_Filter_Block</class> </MageDev_Filter> </blocks> <helpers> <MageDev_Filter> <class>MageDev_Filter_Helper</class> </MageDev_Filter> </helpers> </global> <frontend> <routers> <filter> <use>standard</use> <args> <module>MageDev_Filter</module> <frontName>filter</frontName> </args> </filter> </routers> <layout> <updates> <MageDev_Filter> <file>MageDev_Filter.xml</file> </MageDev_Filter> </updates> </layout> </frontend> </config>
Let’s assume the module will filter products by category and color attribute. The block code goes into app/code/local/MageDev/Filter/Block/Form.php
<?php class MageDev_Filter_Block_Form extends Mage_Core_Block_Template { public function getForm() { $form = new Varien_Data_Form(); $form->setId('filter-form') ->setMethod('GET') ->setAction($this->getUrl('*/*/result')) ->setUseContainer(true); $form->addElement($this->getAttributeSelect('color')->setValue($this->getRequest()->getParam('filterColor', 0))); $form->addElement($this->getCategorySelect()->setValue($this->getRequest()->getParam('filterCategory', 0))); $submit = new Varien_Data_Form_Element_Submit(array('value' => 'Filter')); $submit->setId('submit-filter'); $form->addElement($submit); return $form; } public function getAttributeSelect($attributeCode = null, $attributeLabel = null) { if (null === $attributeCode || !count($options = Mage::helper('MageDev_Filter')->getAttributeOptions($attributeCode))) { return null; } $element = new Varien_Data_Form_Element_Select(); $label = null !== $attributeLabel ? $attributeLabel : $attributeCode; $element->setName('filter' . ucfirst($attributeCode)) ->setId('filter' . ucfirst($attributeCode)); array_unshift($options, array('label' => ' -- All --', 'value' => 0)); $element->setValues($options) ->setLabel(ucfirst($label)); return $element; } public function getCategorySelect() { if (!count($options = Mage::helper('MageDev_Filter')->getCategoryOptions())) { return null; } $element = new Varien_Data_Form_Element_Select(); $element->setName('filterCategory') ->setId('filterCategory'); array_unshift($options, array('label' => ' -- All --', 'value' => 0)); $element->setValues($options) ->setLabel('Category'); return $element; } public function _toHtml() { return $this->getForm()->toHtml(); } }
Note that _toHtml method is defined in the block so it does not require template to generate HTML output. The getAttributeOptions and getCategoryOptions methods used to fill appropriate select elements are defined in app/code/local/MageDev/Filter/Helper/Data.php
<?php class MageDev_Filter_Helper_Data { public function getAttributeOptions($attribute_code) { $attribute = Mage::getModel('catalog/entity_attribute'); $attribute->setStoreId(Mage::app()->getStore()->getId())->loadByCode('catalog_product', $attribute_code); $options = array(); if ($attribute->getData('is_visible')) { $options = Mage::getResourceModel('eav/entity_attribute_option_collection') ->setAttributeFilter($attribute->getId()) ->setStoreFilter() ->load() ->toOptionArray(); } return $options; } protected function _addCategoryOption($categoryData, $levelPrefix = '»', $startAtLevel = 2) { if ($categoryData['level'] >= $startAtLevel) { return array( 'value' => $categoryData['entity_id'], 'label' => str_repeat($levelPrefix, $categoryData['level'] - $startAtLevel) . $categoryData['name'] ); } return null; } public function getCategoryOptions() { $options = array(); $categoryCollection = Mage::getModel('catalog/category')->load(Mage::app()->getStore()->getRootCategoryId())->getCollection()->addAttributeToSelect('name'); foreach ($categoryCollection as $categoryData) { if ($option = $this->_addCategoryOption($categoryData)) { $options[] = $option; } } return $options; } }
_addCategoryOption method accepts three arguments:
- $categoryData – associative array containing data of currently processed category
- $levelPrefix – string added as a prefix of each level of category tree
- $startAtLevel – integer defining level from which category tree elements should be added to options. By default is set to 2 in order to omit root category (at level 1).
Now it’s time to define actions responsible for displaying and processing filter form. Below is the code of module’s index controller located in app/code/local/MageDev/Filter/controller/IndexController.php file:
<?php class MageDev_Filter_IndexController extends Mage_Core_Controller_Front_Action { public function indexAction() { $this->loadLayout(); $this->renderLayout(); } public function resultAction() { $this->loadLayout(); $this->renderLayout(); } }
Peace of cake – it just displays blocks defined in /app/design/frontend/[PackageName]/[ThemeName]/layout/MageDev_Filter.xml
<?xml version="1.0"?> <layout version="0.0.1"> <filter_index_index> <reference name="root"> <action method="setTemplate"><template>page/1column.phtml</template></action> </reference> <reference name="content"> <block type="MageDev_Filter/Form" name="filter_form" /> </reference> </filter_index_index> <filter_index_result> <reference name="content"> <block type="MageDev_Filter/Form" name="filter_form" /> <block type="MageDev_Filter/Result" name="filter_result" template="catalog/product/list.phtml" /> </reference> </filter_index_result> </layout>
As you can see, on results page I used default product list so it will follow general store design. The block code also uses product list functionality by extending Mage_Catalog_Block_Product_List class (app/code/local/MageDev/Filter/Block/Result.php)
<?php class MageDev_Filter_Block_Result extends Mage_Catalog_Block_Product_List { protected function _getProductCollection() { if (is_null($this->_productCollection)) { $collection = Mage::getResourceModel('catalog/product_collection'); Mage::getModel('catalog/layer')->prepareProductCollection($collection); $colorId = $this->getRequest()->getParam('filterColor', 0); $categoryId = $this->getRequest()->getParam('filterCategory', 0); if (!$colorId && !$categoryId) { $this->_redirect('*/*/index'); } if ($colorId) { $collection->addAttributeToFilter('color', $colorId); } if ($categoryId) { $category = Mage::getModel('catalog/category')->load($categoryId); $collection->addCategoryFilter($category, true); } $this->_productCollection = $collection; } return $this->_productCollection; } }
That’s it – you obviously can add as many attributes as you wish. Methods responsible for getting options of attribute and categories might also be useful for other purposes, so you can reuse the helper class in your own modules.



it doesnt seem to work on my part.. i was adding a block in a content but there is no result being shown.. this is the block of code.. {{block type=“gebana_filter/result“ name=“filter_result“ template=“catalog/product/list.phtml“}}
Hello Jason. I assume you use the code to insert block into cms page. Please ensure you call this page with custom parameters (filterColor or filterCategory). You can do it by redirecting form to your cms page or by preparing url to specify these attributes. You can also check if Magento sees your block by adding following method:
to your Gebana_Filter_Block_Result class.
hi man ,
It’s possible to filter the attribute’s value ?
For example : I have a attribute called a ,it’s number, on the frontend page I wanna filter the attribute great then 300(it’s value) not id, any ideas ???
THANKS!
Hi,
Here’s the code that does the trick:
thanks lot !
let me try it !!!!
Thanks for great tutorial
Do you have any idea how to filter attribute’s value like this:
I have a brand’s names (attribute code = brand ) and
wanted to add to filter only brands which relate to existing products in DB
(so if there are no products assigned to attribute, i don’t want include this attribute to filter)…
For categories filter i use getProductCount() function, but i don’t have any idea how to get same for attributes…
Hello! How can I display the filter in sidebar? thanks a lot
Hi Controle_r,
It’s quite easy thanks to Magento layout system. To display form in left column change layout file content to:
You can find more information related to themeing in Designer’s Guide to Magento
Hi Igor,
You can try to use CatalogIndex module and get counts by direct query to catalogindex_eav table or by the use of Mage_CatalogIndex_Model_Attribute::getCount() method. This might not be 100% accurate as it depends on the state of catalog index.
Hope that helps
Hi wonder if you can help me.
i made an Attributes caled ringsize. it shows all avalible ringsize, in the shop by section on the front page,
i like it but, its not the best place for it,.
so i have disabled it there,
what i want is, it to show the ringsize elswhere, in a static block.
or in the shop by but only when i have navigated to the rings section,
best regards ole
Hi Ole,
If you already have a block (static or dynamic) that you are happy with and just want to display it in specific categories, go to Catalog->Manage Categories, select rings category and in Custom Layout Update tab place the ringsize block. You can also specify where this block should be visible by ‘Apply to’ option.
The tutorial is great, but it would have been great if there was ademo ?
thanks
hi,
it doesn’t seem to work for me on Magento 1.4.1 … it’s ok for the search form (need to declare the module in app/etc/module)
but I have error (404 not found) for the result page : if I insert search form in the content of my category, url defined for the form is catalog/category/resultt/?filterColor=7&filterCategory=3&=Filter
I don’t change anything in this original piece of code and I use the default catalog/product/list.phtml template param for result
If someone can help me with a step by step explanation …
Thanks a lot !!!
it works if I replace
setAction($this->getUrl(‘*/*/result’))
by
setAction($this->getUrl(‘filter/index/result’))
If someone can help me to understand … I’m newbie with magento
Just another question … if someone can help me …
How can I add checkbox filter ? ex. if I want to have color attribute in checkbox and not select list … so I can choose 2 or 3 colors in my filter
Thanks a lot !!!
Thanks a lot…
hi,
I tried to add this module, but I have one problem. I dont know how to get result, whent I submit my form. I always get “404 eror,page not found”. I think that I missed someone. Anyone can help? Thanks.
hi i followed the instructions you said,but i cant see this filter at my home page. is there anything else have to do to display this filter? can u plz help me
good tutorial
public function testAction()
{
var_dump(
(string)
Mage::getModel(‘catalog/product’)
->getCollection()
->addFieldToFilter(‘sku’,'your sku’)
->getSelect());
}
may also return the qury!!
check it out
I have followed the instructions but do not see any filter, is this include the display code as well or i have to add something?
Thanks.
Hi,
nice tutorial, exactly what i need.
But, I dont get any output :-/ I tried it on Magento 1.4.1.1
Did you get any idea?
I already cleared the cache and created a xml in /app/etc/modules/ … I also read about confix.xml adminhtml.xml … but I dont get it running : /
Any hints are appreaciated
Cheers,
Matthias
Hy,
Thanks for the elaborate instructions.
Can I add a browsable custom filter using the Magento forms from the backend rather than using code.
Looking forward to getting some light on this.
Best,
Amit