Magento Layered Navigation

Series of articles and tutorials about Magento Layered Navigation.

  • The Architecture of Magento Layered Navigation

    Posted on August 2, 2011 by Engineer-ing

    So how actually does Magento Layered Navigation work?

    It may sound surprising, but a chunk of code that triggers all the functionality of the layered navigation, is placed in "Mage_Catalog_Block_Layer_View", inside the function "_prepareLayout()".

    When is the function _prepareLayout being called?

    The function "_prepareLayout()" exists in every block, and it's being called each time the block is being created.
    Therefore, each time the block "Mage_Catalog_Block_Layer_View" is created, it's function "_prepareLayout()" is being called.

    Here we loop over all the filterable attributes and filter the current product collection by each of them:
    We create an appropriate block for each attribute (for example, for dropdown attribute, we create block from class "Mage_Catalog_Block_Layer_Filter_Attribute") and call its "init" function.

    /* FILE: code/core/Mage/Catalog/Block/Layer/View.php Function:_prepareLayout */
    /**
         * Prepare child blocks
         *
         * @return Mage_Catalog_Block_Layer_View
         */
        protected function _prepareLayout()
        {
            $stateBlock = $this->getLayout()->createBlock($this->_stateBlockName)
                ->setLayer($this->getLayer());
    
            $categoryBlock = $this->getLayout()->createBlock($this->_categoryBlockName)
                ->setLayer($this->getLayer())
                ->init();
    
            $this->setChild('layer_state', $stateBlock);
            $this->setChild('category_filter', $categoryBlock);
    
            $filterableAttributes = $this->_getFilterableAttributes();
            foreach ($filterableAttributes as $attribute) {
                if ($attribute->getAttributeCode() == 'price') {
                    $filterBlockName = $this->_priceFilterBlockName;
                }
                elseif ($attribute->getBackendType() == 'decimal') {
                    $filterBlockName = $this->_decimalFilterBlockName;
                }
                else {
                    $filterBlockName = $this->_attributeFilterBlockName;
                }
    
                $this->setChild($attribute->getAttributeCode() . '_filter',
                    $this->getLayout()->createBlock($filterBlockName)
                        ->setLayer($this->getLayer())
                        ->setAttributeModel($attribute)
                        ->init());
            }
    
            $this->getLayer()->apply();
    
            return parent::_prepareLayout();
        }

    Inside the "init" function we have a call to "_initFilter":

    Here (line 15) we call the apply function on the model that regards the filter block. (e.g:  for the block "Mage_Catalog_Block_Layer_Filter_Attribute", the model will be "Mage_Catalog_Model_Layer_Filter_Attribute")

    /* FILE: code/core/Mage/Catalog/Block/Layer/Filter/Abstract.php Function:_initFilter */
    /**
         * Init filter model object
         *
         * @return Mage_Catalog_Block_Layer_Filter_Abstract
         */
        protected function _initFilter()
        {
            if (!$this->_filterModelName) {
                Mage::throwException(Mage::helper('catalog')->__('Filter model name must be declared.'
                ));
            }
            $this->_filter = Mage::getModel($this->_filterModelName)
                ->setLayer($this->getLayer());
            $this->_prepareFilter();
    
            $this->_filter->apply($this->getRequest(), $this);
            return $this;
        }
     /* FILE: code/core/Mage/Catalog/Model/Layer/Filter/Attribute.php Function:apply */
    
    So finally, the filter is applied to product collection if it exists and has non-empty value in
    the request URI.
    The apply is performed by:
    
    $this->_getResource()->applyFilterToCollection($this, $filter);

    (in the following code chunk).

    /**
    * Apply attribute option filter to product collection
    *
    * @param Zend_Controller_Request_Abstract $request
    * @param Varien_Object $filterBlock
    * @return Mage_Catalog_Model_Layer_Filter_Attribute
    */
    public function apply(Zend_Controller_Request_Abstract $request, $filterBlock)
    {
    $filter = $request->getParam($this->_requestVar);
    if (is_array($filter)) {
    return $this;
    }
    $text = $this->_getOptionText($filter);
    if ($filter && $text) {
    $this->_getResource()->applyFilterToCollection($this, $filter);
    $this->getLayer()->getState()->addFilter($this->_createItem($text, $filter));
    $this->_items = array();
    }
    return $this;
    }

    While "_getResource" returns a resource model of this class (in our example for the class "Mage_Catalog_Model_Layer_Filter_Attribute" "_getResource" will return "Mage_Catalog_Model_Resource_Eav_Mysql4_Layer_Filter_Attribute" object), and there it calls "applyFilterToCollection" that builds appropriate SQL query according to the attribute and its value in URI.

    And thats all,when the loop ends running we have a filtered product collection. And if we go now to "Mage_Catalog_Block_Product_List" (the block that shows the product collection),we will see that the collection is retrieved from layer ("Mage_Catalog_Model_Layer") - this is the object which we have loaded the filtered product collection to it after looping over all filterable attributes.

    Summary

    As we have seen, the architecture of Magento Layered Navigation is simple - the creation of "Mage_Catalog_Block_Layer_View" block triggers looping on models that regards the filterable attributes, and save the filtered product collection back to the layer.

    After understanding the architecture of Magento Layered Navigation we have the power to customize Magento Layered Navigation to our own needs.
    Read the next posts of this series, to see the examples of our new capabilities.


    This post was posted in Magento Layered Navigation and was tagged with layered navigation, filters, sidebar

  • What is Magento Layered Navigation

    Posted on August 2, 2011 by Engineer-ing

    This article is the first article within the sequences of articles/tutorials about Magento Layered Navigation.

    What is Magento Layered Navigation?

    Magento Layered Navigation is a piece of functionality that allows customers to filter categories in the following way:
    If you have to example a category named "Apparel" and the products inside this category have
    attributes such as color, manufacturer, etc., you can tell Magento to display a sidebar that will allow the customers to filter the category named "Apparel" by those attributes (and some others - will be explained later in this article). The next image shows an example of such sidebar:

    Example of Sidebar

    How to make such a sidebar appear on my desired category?

    To make such a sidebar appear on a desired category and contain a desired attribute, you must follow next 3 simple steps:

    1) Make the desired category "anchor": Go to the admin panel -> Catalog -> Manage Categories. On the left, click on the category that you want to make filterable. On the right, click on the "Display Settings" tab, and choose "Yes" on "In Anchor" field.

    2) Add filterable attributes: Go  to the admin panel ->Catalog -> Attributes -> Manage Attributes.  Add attribute which you wish to filter by (Detailed explanation about adding/managing  attributes -here). Make sure that you choose "Filterable (without results)" or" Filterabe (with results)" in"Use In Layered Navigation" field (To do so, you must choose "Dropdown" or "Multiple Select" or "Price"  in "Catalog Input Type for Store Owner" field).  Also don't forget to add some options (on Manage Label/Options tab). Finaly, make sure that you added those attributes to some attribute set (Detailed explanation about adding/managing  attribute sets - in next articles), remember the name of this set for next section.

    3)Apply attributes on some products: if in the previous section, you set "Filterable (without results)" for the attribute, you will not see this attribute on the sidebar, until you will have some products that has a value for this attribute. So, create new product (choose tha attribute set that you chose in a previous section), or update existing product (that has this attribute on it's attribute set), so that the field of the attribute(s) you added, will contain some value. Don't forget to set a desired category as a category of this product.

    Now, on your desired category page, you should see a sidebar, that contains the attribute you added.

    (Note: those 3 steps are 100% effective for clean Magento installation. If some modifications where done to the code/design files, those steps may not suffice.)

    In the next article we will learn about the architecture of a Magento Layered Navigation.

     


    This post was posted in Magento Layered Navigation and was tagged with layered navigation, side bar, filters

2 Item(s)

Magento is a well-engineered eCommerce platform designed to help engineers develop customized eCommerce online stores. Due to lack of proper coding documentation, Engineer-ing.com was created with the sole purpose of instructing Magento developers to-be with the "how-to-do" know-how. In the event of unresolved issues, you are more than welcome to contact me for consultation. However, please do so only if you possess a Software Engineering background and you're able to specify your question.