Magento – Adding Product Filterable Attributes to the Category navigation

attributesfilterable-attributesproduct-attribute

I am doing a task of adding filterable attributes and their options to the category navigation menu.for this i got Marius article.Thanks to Marius.Now everything is done except i need one more attribute as By Category which will have the sub-category of the parent Category.

for example.

Mobiles
    - Feature Phones
    - Smart Phones

till now my work results following result

Mobile
    - Feature Phone

    - Smart Phone

    - By Brand
        - Apple
        - Nokia
        - Samsung
        - LG

    - By Operating System
        - Android
        - Windows
        - Linux

What i need is

Mobile
    - By Category
        - Feature Phone
            - Sub Category 1(if any)
            - Sub Category 2(if any)
        - Smart Phone

    - By Brand
        - Apple
        - Nokia
        - Samsung
        - LG

    - By Operating System
        - Android
        - Windows
        - Linux

My code is

public function addAttributes($observer){
        $menu = $observer->getMenu();
        $tree = $menu->getTree();

        foreach($menu->getChildren() as $child){
            $nodeId = $child->getId();

            if(substr($nodeId, 0,strlen('category-node-')) == 'category-node-'){
                $id = str_replace('category-node-', '', $nodeId);
                $mainUrl = $child->getUrl();
                //echo $mainUrl; exit;
                $attributes = $this->getAttributes($id);
                foreach($attributes as $attribute){
                    //echo $attribute->getAttributeCode();
                    //mobile_bycategory
                    $cat_options = array();
                    if($attribute->getAttributeCode() =='mobile_bycategory'){
                        $children = Mage::getModel('catalog/category')->getCategories($id);
                        $i = 0;
                        foreach($children as $category){
                            $cat_options[$i]['value'] = $category->getId();
                            $cat_options[$i]['label'] = $category->getName();
                            $cat_options[$i]['url'] = $category->getRequestPath();
                            $i++;
                        }
                        //echo '<pre>'; print_r($options); exit;
                    }

                    if($attribute->getAttributeCode() =='mobile_bycategory'){
                        $options = $cat_options;
                    }else{
                        $options = $attribute->getSource()->getAllOptions();    
                    }
                    //echo '<pre>'; print_r($options); exit;

                    if (count($options) > 0) {
                        $attrNodeId = 'attribute-'.$id.'-'.$attribute->getId();
                        $data = array(
                            'name' => Mage::helper('neo_topnav')->__('By %s', $attribute->getFrontendLabel()),
                            'id' => $attrNodeId,
                            'url' => '#',
                            'is_active' => false
                        );
                        $attrNode = new Varien_Data_Tree_Node($data, 'id', $tree, $menu);
                        //for each option add a new sub menu
                        foreach ($options as $option) {
                            if ($option['value']) {
                                $optionNodeId = 'attribute-'.$id.'-'.$attribute->getId().'-'.$option['value'];
                                $base_url = Mage::getBaseUrl();
                                if($attribute->getAttributeCode() =='mobile_bycategory'){ $final_url = $base_url.$option['url']; } else { $final_url = $mainUrl.'?'.$attribute->getAttributeCode().'='.$option['value']; }
                                $data = array(
                                    'name' => $option['label'],
                                    'id' => $optionNodeId,
                                    'url' => $final_url, 
                                    'is_active' => false
                                );
                                $optionNode = new Varien_Data_Tree_Node($data, 'id', $tree, $menu);
                                $attrNode->addChild($optionNode);
                            }
                        }
                        $child->addChild($attrNode);
                    }
                }
            }
        }
    }

I had succefully done until

Mobiles
 - By Category
  - Feature Phone
  - Smart Phone

I was stock on the Sub catogry of Subcatogy (if any)

Mobile
        - By Category
            - Feature Phone
                - Sub Category 1(if any)
                - Sub Category 2(if any)
            - Smart Phone

Please Help Me.

Best Answer

Based on the solution provided by Marius and some code I had locally i put the below together.

Since the categories are added via an observer we simply disable the default observer and create own own version based on the original that adds the new filters in and pops the default categories under a "By Category" node when the Category level is 2.

Menu Image

    <page_block_html_topmenu_gethtml_before>
        <observers>
            <catalog_add_topmenu_items>
                <type>disabled</type>
            </catalog_add_topmenu_items>

            <neo_topnav>
                <class>neo_topnav/observer</class>
                <method>pageBlockHtmlTopmenuGethtmlBefore</method>
            </neo_topnav>
        </observers>
    </page_block_html_topmenu_gethtml_before>

/**
 * Adds catalog categories to top menu
 *
 * @param Varien_Event_Observer $observer
 */
public function pageBlockHtmlTopmenuGethtmlBefore(Varien_Event_Observer $observer)
{
    $block = $observer->getEvent()->getBlock();
    $block->addCacheTag(Mage_Catalog_Model_Category::CACHE_TAG);
    $this->_addCategoriesToMenu(
        Mage::helper('catalog/category')->getStoreCategories(), $observer->getMenu(), $block, true
    );
}

/**
 * Recursively adds categories to top menu
 *
 * @param Varien_Data_Tree_Node_Collection|array $categories
 * @param Varien_Data_Tree_Node $parentCategoryNode
 * @param Mage_Page_Block_Html_Topmenu $menuBlock
 * @param bool $addTags
 */
protected function _addCategoriesToMenu($categories, $parentCategoryNode, $menuBlock, $addTags = false)
{
    $categoryModel = Mage::getModel('catalog/category');
    foreach ($categories as $category) {
        if (!$category->getIsActive()) {
            continue;
        }

        $nodeId = 'category-node-' . $category->getId();

        $categoryModel->setId($category->getId());
        if ($addTags) {
            $menuBlock->addModelTags($categoryModel);
        }

        $tree = $parentCategoryNode->getTree();

        $categoryData = array(
            'name' => $category->getName(),
            'id' => $nodeId,
            'url' => Mage::helper('catalog/category')->getCategoryUrl($category),
            'is_active' => $this->_isActiveMenuCategory($category)
        );
        $categoryNode = new Varien_Data_Tree_Node($categoryData, 'id', $tree, $parentCategoryNode);

        $parentCategoryNode->addChild($categoryNode);

        $flatHelper = Mage::helper('catalog/category_flat');
        if ($flatHelper->isEnabled() && $flatHelper->isBuilt(true)) {
            $subcategories = (array)$category->getChildrenNodes();
        } else {
            $subcategories = $category->getChildren();
        }

        switch($category->getLevel()){
            case 2:
                $byCategoryNode = $this->_addShopByCategory($parentCategoryNode, $category, $tree, $categoryNode);
                $this->_addCategoriesToMenu($subcategories, $byCategoryNode, $menuBlock, $addTags);
                $this->_addFiltersToMenu($category->getId(),$categoryNode,$tree,$parentCategoryNode);
                break;
            default:
                $this->_addCategoriesToMenu($subcategories, $categoryNode, $menuBlock, $addTags);
                break;
        }

    }
}

/**
 * @param $parentCategoryNode
 * @param $category
 * @param $tree
 * @param $categoryNode
 * @return Varien_Data_Tree_Node
 */
protected function _addShopByCategory($parentCategoryNode, $category, $tree, $categoryNode)
{
    $byCategoryNodeId = 'by-category-node-' . $category->getId();
    $byCategoryData = array(
        'name' => Mage::helper('neo_topnav')->__('By Category'),
        'id' => $byCategoryNodeId,
        'url' => '#',
        'is_active' => false
    );
    $byCategoryNode = new Varien_Data_Tree_Node($byCategoryData, 'id', $tree, $parentCategoryNode);
    $categoryNode->addChild($byCategoryNode);
    return $byCategoryNode;
}

/**
 * @param $id
 * @param $category
 * @param $tree
 * @param $parentCategoryNode
 */
function _addFiltersToMenu($id,$category,$tree,$parentCategoryNode){
    $attributes = $this->getAttributes($id);
    $mainUrl = $category->getUrl();
    foreach ($attributes as $attribute) {
        //get the options for each attribute
        $options = $attribute->getSource()->getAllOptions();
        if (count($options) > 0) {
            //add "By ..." menu item - non clickable
            $attrNodeId = 'attribute-'.$id.'-'.$attribute->getId();
            $data = array(
                'name' => Mage::helper('neo_topnav')->__('By %s', $attribute->getFrontendLabel()),
                'id' => $attrNodeId,
                'url' => '#',
                'is_active' => false
            );
            $attrNode = new Varien_Data_Tree_Node($data, 'id', $tree,$parentCategoryNode);
            //for each option add a new sub menu
            foreach ($options as $option) {
                if ($option['value']) {
                    $optionNodeId = 'attribute-'.$id.'-'.$attribute->getId().'-'.$option['value'];
                    $data = array(
                        'name' => $option['label'],
                        'id' => $optionNodeId,
                        'url' => $mainUrl.'?'.$attribute->getAttributeCode().'='.$option['value'],
                        'is_active' => false
                    );
                    $optionNode = new Varien_Data_Tree_Node($data, 'id', $tree,$parentCategoryNode);
                    $attrNode->addChild($optionNode);
                }
            }
            $category->addChild($attrNode);
        }
    }
}

//get allowed attributes in that category
public function getAttributes($categoryId) {
    $layer = Mage::getModel("catalog/layer");
    $layer->setCurrentCategory(Mage::getModel('catalog/category')->load($categoryId));
    $validAttributes = array();
    foreach ($layer->getFilterableAttributes() as $attribute) {
        //allow only select attributes - you can implement your additional filters here
        if ($attribute->getFrontendInput() == 'select'){
            $validAttributes[] = $attribute;
        }
    }
    return $validAttributes;
}


/**
 * Checks whether category belongs to active category's path
 *
 * @param Varien_Data_Tree_Node $category
 * @return bool
 */
protected function _isActiveMenuCategory($category)
{
    $catalogLayer = Mage::getSingleton('catalog/layer');
    if (!$catalogLayer) {
        return false;
    }

    $currentCategory = $catalogLayer->getCurrentCategory();
    if (!$currentCategory) {
        return false;
    }

    $categoryPathIds = explode(',', $currentCategory->getPathInStore());
    return in_array($category->getId(), $categoryPathIds);
}

}