atwix

How to add a new item to the navigation menu in Magento

Adding a new item to the navigation menu is fairly simple.

We will add this ability to a custom module. There is no need to modify core Magento files or templates and layouts.

First, we need to rewrite the block that is responsible for menu drawing; however, we will not be using a <rewrite> tag. The reason for this is, if the module is disabled, Magento will break. Instead, will will use an observer for it.

At global scope, in your config.xml file, add the following code:

<events>
  <controller_action_layout_generate_blocks_after>
    <observers>
      <allproducts>
        <type>singleton</type>
        <class>Company_ModuleName_Model_Observer</class>
        <method>regeneratemenu</method>
      </allproducts>
    </observers>
  </controller_action_layout_generate_blocks_after>     
</events>

You can also use other events instead of controller_action_layout_generate_blocks_after. Next, you must create Observer.php in the Model folder using method regenerateMenu

class Company_ModuleName_Model_Observer
{

public function regenerateMenu($observer)
{
  // if module is active
  if (!Mage::getStoreConfig('advanced/modules_disable_output/Company_ModuleName'))
  {
    $layout = Mage::getSingleton('core/layout');

    // remove all the blocks you don't want
    $layout->getUpdate()->addUpdate('<remove name="catalog.topnav"/>');

    // load layout updates by specified handles
    $layout->getUpdate()->load();

    // generate xml from collected text updates
    $layout->generateXml();

    // generate blocks from xml layout
    $layout->generateBlocks();

  }
}
}

Next step: you should create the block file Navigation.php with the following code:

class Company_ModuleName_Block_Navigation extends Mage_Catalog_Block_Navigation
{

public function renderCategoriesMenuHtml($level = 0, $outermostItemClass = '', $childrenWrapClass = '') 
{

  // if route is active
  $active = ($this->getRequest()->getRouteName()=='yourroute' ? 'active' : '');

  // Get navigation menu html
  $html = parent::renderCategoriesMenuHtml($level, $outermostItemClass, $childrenWrapClass);

  // if module is active
  if (Mage::helper('core/data')->isModuleEnabled('Company_ModuleName')) {
    // Adding new menu item. You can also add few items or child elements there
    $html .=  "<li class='$outermostItemClass $active'><a class='$outermostItemClass' href='" . Mage::getUrl('yourroute') . "'><span>" . Mage::helper('yourroute')->__('New Menu Item') . "</span></a></li>";
  }
  return $html;
}
}

Note: Don’t forget to declare the block in your config.xml file.
After, you need to declare the path for your layout XML in config:

<layout>
  <updates>
    <yourroute>
      <file>newmenuitem.xml</file>
    </yourroute>
  </updates>
</layout>

Lastly, here is content for newmenuitem.xml

<?xml version="1.0"?>
<layout version="0.1.0">
  <default>
    <reference name="top.menu">
     <!--Note: use custom block file for menu drawing and default template file-->
      <block type="yourroute/navigation" name="newmenu.topnav" template="catalog/navigation/top.phtml">
      </block>
    </reference>
  </default>
</layout>

Now you will have successfully added a menu item without modifying core files.
Happy coding!


  • Dane

    Hi,

    thanks, first of all, for that nice code.
    To complete it for me, please tell me where to put the files Observer.php and Navigation.php. Should they be under /app/code/community/CompanyName/ModuleName?

    And the newmenuitem.xml should be under /app/design/frontend/default/theme/layout or?

    Where should I add the “After, you need to declare the path for your layout XML in config” part? Questions over questions, hopefully, you can help me.

    Thanks and best regards,
    Dane

  • Nick Kravchuk

    Hi Dane!

    Thanks for your comment.
    Let’s see:
    1. file Observer.php should be there /app/code/community/CompanyName/ModuleName/Model/Observer.php
    2. file Navigation.php should be there /app/code/community/CompanyName/ModuleName/Block/Navigation.php
    Note: don’t forget to declare block in your /app/code/community/CompanyName/ModuleName/etc/config.xml whithin the tag see below

    CompanyName_ModuleName_Block

    3. the newmenuitem.xml should be under /app/design/frontend/default/theme/layout, you’re right.
    4. the “After, you need to declare the path for your layout XML in config” part you should add to the config.xml within the tag

    Hopefully this help you. Thanks a lot.

  • Elegant Rao

    greaaaaaaaat , been looking for this. good job Nick.

    • Rao

      $layout->getUpdate()->load(); this will break the content on my account. must comment it in the observer action(regrenratemenu) .

      • Anonymous

        Could you provide the text of error? Works fine for me. Thanks.

  • CH

    I am a lot of lost here, just learning about magento and not a programer by any means. So something is missing…

    1. created config.xml in /app/code/community/CompanyName/ModuleName/etc/ with this content –

    singleton
    Company_ModuleName_Model_Observer
    regeneratemenu

    newmenuitem.xml

    2. created Observer.php in /app/code/community/CompanyName/ModuleName/Model/ with this content –

    class Company_ModuleName_Model_Observer
    {

    public function regenerateMenu($observer)
    {
    // if module is active
    if (!Mage::getStoreConfig(‘advanced/modules_disable_output/Company_ModuleName’))
    {
    $layout = Mage::getSingleton(‘core/layout’);

    // remove all the blocks you don’t want
    $layout->getUpdate()->addUpdate(”);

    // load layout updates by specified handles
    $layout->getUpdate()->load();

    // generate xml from collected text updates
    $layout->generateXml();

    // generate blocks from xml layout
    $layout->generateBlocks();

    }
    }
    }

    3. created Navigation.php in /app/code/community/CompanyName/ModuleName/Block/ with this content –

    class Company_ModuleName_Block_Navigation extends Mage_Catalog_Block_Navigation
    {

    public function renderCategoriesMenuHtml($level = 0, $outermostItemClass = ”, $childrenWrapClass = ”)
    {

    // if route is active
    $active = ($this->getRequest()->getRouteName()==’yourroute’ ? ‘active’ : ”);

    // Get navigation menu html
    $html = parent::renderCategoriesMenuHtml($level, $outermostItemClass, $childrenWrapClass);

    // if module is active
    if (Mage::helper(‘core/data’)->isModuleEnabled(‘Company_ModuleName’)) {
    // Adding new menu item. You can also add few items or child elements there
    $html .= “” . Mage::helper(‘yourroute’)->__(‘New Menu Item’) . ““;
    }
    return $html;
    }
    }

    4. created newmenuitem.xml in /app/design/frontend/default/theme/layout with this content –

    • Anonymous

      Sorry, but your question is too broad. This goes beyond the community support we provide in comments.

  • vignesh babu

    please mention me the path where to store these files in magento

  • Dmitry

    Sorry

    I don’t undersand “The reason for this is, if the module is disabled, Magento will break. Instead, will will use an observer for it.”

    This Example_Module must be depend of Mage_Catalog, but who will disable Mage_Catalog?

    • NickKravchuk

      I mean if we use rewrite block logic then magento will be broken when we disable Example_Module. Mage catalog module will not be disabled by anybody, I hope :)