Magento 1.7 – How to Add Links to Category Menu

magento-1.7menu

I know this is an old question with a lot of answers, most of them suggest editing the menu template but that does not work for me. There is no need to explain why, let's say I'm a bit OCD about doing things the "right" way.
This question may serve as material for others in need. Here goes.
Starting with Magento 1.7 the top menu is built using an event: page_block_html_topmenu_gethtml_before. This is used by the Mage_Catalog module to add the categories. I want to use that. For this I've created an observer in my module like this:

<global>
    <events>
        <page_block_html_topmenu_gethtml_before>
            <observers>
                <my_observer>
                    <class>mymodule/observer</class>
                    <method>addItemsToTopmenuItems</method>
                </my_observer>
            </observers>
        </page_block_html_topmenu_gethtml_before>
    </events>
</global>

In the Observer.php class I have

public function addItemsToTopmenuItems($observer){
    //get the menu object: //Type Varien_Data_Tree_Node
    $menu = $observer->getMenu();
    //get the tree object in the menu //type Varien_Data_Tree
    $tree = $menu->getTree();
    //get current page handler
    $action = Mage::app()->getFrontController()->getAction()->getFullActionName();
    $brandNodeId = 'category-node-brand';
    //set the node id, label and url
    $data = array(
        'name' => Mage::helper('catalog')->__('Brands'),
        'id' => $brandNodeId,
        'url' => Mage::getUrl('brands'),
        'is_active' => ($action == 'brands')
    );
    //create a node object
    $brandNode = new Varien_Data_Tree_Node($data, 'id', $tree, $menu);
    //add the node to the menu
    $menu->addChild($brandNode);
    return $this;
}

My observer has some other code that adds all the brands as sub-items of the Brands menu but there is no need for that here.
This works perfectly and adds a menu Brands as a last item in the menu.
The issue is that I want this as the first menu and my observer is called after the observer in Mage_Catalog that adds the categories. Since there is no way (not that I know of) to sort the order of the observers on an event …I have a problem
[EDIT]
As @Benmarks suggested I made the Mage_Catalog module depend on my module and now my menu item is the first in the list. But I still have to add menus between the categories and at the end. Creating a new module would probably solve the problem with the items at the end of the menu, but I still have an issue with the ones between categories,
[/EDIT]
So basically my question resumes to "(How) can I move around child nodes of a Varien_Data_Tree_Node in a Varien_Data_Tree object?"
Please don't suggest adding the Brands as the first category in the category tree. This is not an option (like I said…my OCD about these things).

Best Answer

After struggling a bit here is the solution I found. I let my observer execute after the one in Mage_Catalog and decided to re-create the menu entirely.
The main idea is to get all the existing menu items put them in a temporary array, remove them from the menu then add my links between the existing items and in the end add all items to the menu again. Something like this:

public function addItemsToTopmenuItems($observer){
    //get the menu object -Type Varien_Data_Tree_Node
    $menu = $observer->getMenu();
    //get the tree object in the menu -type Varien_Data_Tree
    $tree = $menu->getTree();
    //get current page handler
    $action = Mage::app()->getFrontController()->getAction()->getFullActionName();
    $brandNodeId = 'category-node-brand';
    //set the node id, label and url
    $data = array(
        'name' => Mage::helper('catalog')->__('Brands'),
        'id' => $brandNodeId,
        'url' => Mage::getUrl('brands'),
        'is_active' => ($action == 'brands')
    );
    //create a node object
    $brandNode = new Varien_Data_Tree_Node($data, 'id', $tree, $menu);
    //temporary array with nodes
    $menuItems = array();
    //my first menu item
    $menuItems[] = $brandNode;
    //loop through existing menu items, add them to the array and remove them from the    menu
    foreach ($menu->getChildren() as $child){
        //add the item to the temp array
        $menuItems[] = $child;
        //remove item from the menu
        $menu->removeChild($child);
        //I need to add a new menu item after the category with id 6
        //don't worry the id is not hard coded. it comes from a config setting
        //I just added 6 here to make it easier to read
        if ($child->getId() == 'category-node-6'){
            //create a new node as $brandNode called $newNode
            ...
            //add the node to my temp array
            $menuItems[] = $newNode;
        }
    }
    //add other nodes at the end of my temp array
    ...
    //recreate the menu in the order I need
    foreach ($menuItems as $child){
        $menu->addChild($child);
    }
}

This seams to solve my problem,but I'm hoping for a more elegant method of doing it.

Related Topic