Hi everybody. Today I’ll write about layered navigation – the very important part of each e-commerce website. The layered navigation helps your customers easily find products with the attribute(s) that they need. 4 main following parts will be covered:

  • Add attribute to layered navigation
  • Add the custom source to attribute
  • Class and database used in layered navigation
  • Custom layered navigation

1. Add attribute to layered navigation

To add an attribute to the layered navigation, the attribute needs to satisfy both of the followings:

  • Having Dropdown or Multiple Select or Price catalog input type
  • Enabled to use in layered navigation

You can easily edit an attribute in the backend then change the attribute shown in layered navigation.

2. Add the custom source to attribute

When you add an attribute by the source code, you can add a custom source to that attribute. For example:

$setup = new Mage_Catalog_Model_Resource_Setup('core_setup');
$setup->addAttribute('catalog_product','cover',array(
'group' => 'General',
'type' => 'int',
'input' => 'select',
'label' => 'cover',
'source' => 'test/source_cover',// custom source for attribute
'backend' => '',
'frontend' => '',
'visible' => 1,
'user_defined' => 1,// enable edit by admin
'is_filterable' => 1,// Attribute used in Layered Navigation
'visible_on_front' => 1,
'is_configurable' => 1,
));

Then, you need to write the source model for an attribute as below:

<?php

class Magestore_Test_Model_Source_Shopid extends Mage_Eav_Model_Entity_Attribute_Source_Abstract
{
/**
* Retrieve all cover option
*/
public function getAllOptions($withEmpty = true){
return array(
array(
'value' => 1,
'label' => 'Black'
),
array(
'value' => 2,
'label' => 'White'
),
);
}
}

The function getAllOptions is used to get all available options for the attribute. You can customize this function to define options for your attribute.

3. Class and database used in layered navigation

In layered navigation, Mage_Catalog_Model_Layer is the important class. It provides product collection for layers, filter collection to be shown in the list… Besides, there are other classes used in layered navigation such as:

  • Mage_Catalog_Model_Layer_State: the state of layered navigation
  • Mage_Catalog_Block_Layer_View:  the block to show layered navigation in frontend

To improve the performance, Magento uses an index table to filter and count the result for each filter item. The index table for the general attribute is catalog/product_index_eav:

For price attribute, it is table catalog/product_index_price:

4. Custom layered navigation

When you show a custom list of product and want to add the layered navigation to filter your product list, at first, you have to write your own a layer model. For instance:

<?php

class Magestore_Test_Model_Layer extends Mage_Catalog_Model_Layer
{
public function getProductCollection(){
// get your custom product collection
return Mage::getResourceModel('catalog/product_collection');
}
}

In this model, you can write your own method getProductCollection to show your custom product list.
After that, you need to write your custom layer view block:

<?php

class Magestore_Test_Block_Layer_View extends Mage_Catalog_Block_Layer_View
{
protected function _construct(){
parent::_construct();
Mage::register('current_layer', $this->getLayer());
}

public function getLayer(){
return Mage::getSingleton('test/layer');
}
...
}

There’s one important point that in this block you need to write the methods getLayer (to get your custom layer for your layered navigation) and  _construct (to register your layer with system).

Finally, you may add the layer block to your layout and refresh your page to view the result:

<layout>
<test_product_list>
<reference name="left">
<!-- here is your custom layered navigation -->
<block type="test/layer_view" name="customlayer" after="currency" template="catalog/layer/view.phtml"/>
</reference>
<reference name="content">
<block type="catalog/product_list" name="product_list" template="catalog/product/list.phtml">
<block type="catalog/product_list_toolbar" name="product_list_toolbar" template="catalog/product/list/toolbar.phtml">
<block type="page/html_pager" name="product_list_toolbar_pager"/>
</block>
</block>
</reference>
</test_product_list>
...
</layout>

My tutorial today seems so long, right? Thus I’m about to end it here. Many thanks for reading! I’ll really happy if you can leave comments and tell me what you think about it. See you again!

Author

Why Magestore? We believe in building a meaningful & long-term relationship with you.

10 Comments

    • Thanks Derrik for your comment. Certainly we’ll keep Magento Certificate series on. Remember to follow it!

  1. Good post, but when catalog product flat enabled, my custom attribute not shown on layered nav. You have any idea?

    • Hello,
      To show the layered navigation, when you create your Custom Attribute, you have to set “Use In Layerd Navigation” to “Filterable”. After your Custom Attribute is generated already, you need to reindex the data and refresh cache also.
      Let me know if there’s any other question. Have a nice day!

  2. Hi .Thanks for the great post. I am facing a problem. I have successfully setup layered navigation for my custom collection. But when I click on any filter it shows all result . It does not filter my collection. What am I doing wrong?

    • Hi Ashish,
      Your collection to show in your template is gotten from your Layer model, not directly from the resouce collection. Review it and you can solve the problem.

  3. Hi David,

    I am attempting to do the following, however I am suffering from some problems. In my layer model in which I extend ‘Mage_Catalog_Model_Layer’ I do the following:

    public function getProductCollection()
    {
    $attCode = ‘brand’;
    $optId = 359;

    if ( isset( $this->_attProductCollections[$attCode][$optId] ) ) {
    $collection = $this->_attProductCollections[$attCode][$optId];
    } else {
    $collection = $this->getCurrentCategory()->getProductCollection();
    $this->prepareProductCollection($collection);
    $this->_attProductCollections[$attCode][$optId] = $collection;
    }

    return $collection;
    }

    In the following function, i then filter my specific results by the brand i have hard coded as follows:

    public function prepareProductCollection( $collection )
    {
    $attCode = ‘brand’;
    $optId = 359;

    // Add the filters here
    $collection
    ->addAttributeToSelect( Mage::getSingleton( ‘catalog/config’ )->getProductAttributes() )
    ->addFieldToFilter( array(
    array( ‘attribute’ => $attCode, ‘eq’ => $optId ),
    ) )
    ->addMinimalPrice()
    ->addFinalPrice()
    ->addTaxPercents()
    ->addUrlRewrite($this->getCurrentCategory()->getId());

    Mage::getSingleton(‘catalog/product_status’)->addVisibleFilterToCollection($collection);
    Mage::getSingleton(‘catalog/product_visibility’)->addVisibleInCatalogFilterToCollection($collection);

    return $this;
    }

    Everything else is as you have listed above, however, when I try to load the page, i get the following error:

    SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘e.brand’ in ‘where clause’

    Would you know what is causing this?

    • Hi Peter,

      You can try changing
      ->addFieldToFilter( array(
      array( ‘attribute’ => $attCode, ‘eq’ => $optId ),
      ) )
      to
      ->addFieldToFilter($attCode, array(‘eq’ => $opId))

      Nice code!

  4. For the people interested in doing this take note of the following!

    If your custom product collection is filtered by a specific attribute, you must remember to remove that attribute option from the actual layered nav menu.

    To do this, you will want to modify the following:
    In your Layer Model that extends ‘Mage_Catalog_Model_Layer’, in relation to the guide above it would be ‘Magestore_Test_Model_Layer’ you must re-write the following function:


    protected function _prepareAttributeCollection($collection)
    {
    parent::_prepareAttributeCollection($collection);

    $attCode = '';
    $collection->addFieldToFilter('attribute_code', array( 'neq' => $attCode ) );

    return $collection;
    }

    Hope this helps 😉

Write A Comment