18

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).

  • Is there a reason to not create empty categories and only show an CMS block? - I accept "yes" as an answer, just to be sure, you thought about this :) – Fabian Blechschmidt Sep 1 '13 at 15:06
  • 1
    @FabianBlechschmidt."Yes". There is no point in getting in details, but there are reasons (some of them may be pretty stupid). I don't make the "rules" on this one. So it has do be done like this. – Marius Sep 1 '13 at 15:34
  • @FabianBlechschmidt: To clear some of the "fog" here is a good reason so you won't say I'm stubborn (even if I am). The custom links (brands, new, sales and others) are not in the same place for all store views. But all store views use the same categories. So I don't want to create a different tree for each store view. By using an observer, I can read some config settings and arrange the items as I want. I can even add the menu items depending on the store view id. – Marius Sep 2 '13 at 6:58
  • Also interested, I have the same problem, my solution is to override Varien_Data_Tree_Node_Collection and adding here a method to sort nodes as I want, but i'm looking for a cleaner way. – Fab Oct 5 '13 at 16:52
9

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.

  • Marius is ther any other observer other than page_block_html_topmenu_gethtml_before to add links to category menu ? – Pradeep Sanku Jan 12 '15 at 18:09
  • Reason is i had used the same observer but i had an extension for megamenu which is not calling page_block_html_topmenu_gethtml_before.please let me know – Pradeep Sanku Jan 12 '15 at 18:10
  • @PradeepSanku. I don't know any other event. Maybe the extension you are using replaces completely the main menu. You should ask the developer for support. – Marius Jan 12 '15 at 20:48
4

Since there is no way (not that I know of) to sort the order of the observers on an event ...I have a problem.

Ah, but there is! In your module declaration file, set the dependency of Mage_Catalog to your module, e.g.:

<modules>
    <Your_Module>
        <active>true</active>
        <codePool>local</codePool>
    </Your_Module>
    <Mage_Catalog>
        <depends>
            <Your_Module/>
        </depends>
    </Mage_Catalog>
</modules>

Also, you could (I think) copy the catalog observer config to the frontend event area and deactivate it in the global area. My assumption is that global events are processed before frontend events. (I've no idea why this event is configured under global in the first place.)

And of course there are other options which you can effect through rewrites and PHP.

  • Thanks. You have my +1 here. I actually moved my event inside the <global> tag, I declared Mage_Catalog to depend on my module (even if I feel a little 'dirty' doing that) and it works, but I end up with an other issue. I need some other menu items placed between some categories and at the end of the menu so I still need to be able to move them arround. I will update the question. – Marius Aug 29 '13 at 15:06
-1
  1. create new subcategory
  2. change the link with javascript, ex your new category is "75"
jQuery(document).ready(function(){
    document.getElementById("menu75").getElementsByTagName("a")[0].href =

"https://YourNewLinkink"; });

  • and how do I know the id of the element if it's supposed to work with any id? This is not a viable solution. I will not call it even a workaround. – Marius Sep 28 '17 at 10:54
  • it works on my code... :) – zatanabee Sep 28 '17 at 16:56
  • the id show @ Catalog>manage categories – zatanabee Sep 28 '17 at 16:58

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.