Nested XML Menus using addLink() in Magento

Natively Magento doesn’t have a direct method to nest menu items using the page/template_links->addLink() function, but we had some menus that will never change and are best set in the layout XML rather than in the database/CMS. There is a feature to addLinkBlock() – but this relies on the block outputting the wrapping <li> – but as we intended to nest addLink(), this wouldn’t quite work.

So we put together a tiny extension to offer this functionality,

The files

You’ll need to create the following files and directories first,

./app/code/community/Sonassi/TemplateLinks
./app/code/community/Sonassi/TemplateLinks/etc
./app/code/community/Sonassi/TemplateLinks/Block/Page/Template

./app/etc/modules/Sonassi_TemplateLinks.xml
./app/code/community/Sonassi/TemplateLinks/etc/config.xml
./app/code/community/Sonassi/TemplateLinks/Block/Page/Template/Links.php
./app/design/frontend/x/x/template/page/template/links_nested.phtml

Add this to ./app/etc/modules/Sonassi_TemplateLinks.xml

<?xml version="1.0"?>
<!--
/*
* @category    Module
* @package     Sonassi_TemplateLinks
* @copyright   Copyright (c) 2012 Sonassi
*/
-->
<config>
  <modules>
    <Sonassi_TemplateLinks>
      <active>true</active>
      <codePool>community</codePool>
    </Sonassi_TemplateLinks>
  </modules>
</config>

Add this to ./app/code/community/Sonassi/TemplateLinks/etc/config.xml

<?xml version="1.0"?>
<!--
/*
* @category    Module
* @package     Sonassi_TemplateLinks
* @copyright   Copyright (c) 2012 Sonassi
*/
-->
<config>
  <modules>
    <Sonassi_TemplateLinks>
      <version>1.0.0</version>
      <depends/>
    </Sonassi_TemplateLinks>
  </modules>
  <global>
    <blocks>
      <templatelinks>
        <class>Sonassi_TemplateLinks_Block</class>
      </templatelinks>  
    </blocks>
  </global>
</config>

Add this to ./app/code/community/Sonassi/TemplateLinks/Block/Page/Template/Links.php

<?php

/*
 * @category    Module
 * @package     Sonassi_TemplateLinks
 * @copyright   Copyright (c) 2012 Sonassi
 */

class Sonassi_TemplateLinks_Block_Page_Template_Links extends Mage_Page_Block_Template_Links
{
    protected function _construct()
    {
        $this->setTemplate('page/template/links_nested.phtml');
    }
    
    
    public function addLink($label, $url = '', $title = '', $prepare = false, $urlParams = array(), $position = null, $liParams = null, $aParams = null, $beforeText = '', $afterText = '', $childMenu = false)
    {
        if (is_null($label) || false === $label) {
            return $this;
        }
        
        $link = new Varien_Object(array(
            'label' => $label,
            'url' => ($prepare ? $this->getUrl($url, (is_array($urlParams) ? $urlParams : array())) : $url),
            'title' => $title,
            'li_params' => $this->_prepareParams($liParams),
            'a_params' => $this->_prepareParams($aParams),
            'before_text' => $beforeText,
            'after_text' => $afterText,
            'child_menu' => ($childMenu ? $this->getLayout()->getBlock($childMenu) : '')
        ));
        
        $this->_links[$this->_getNewPosition($position)] = $link;
        if (intval($position) > 0) {
            ksort($this->_links);
        }
        
        return $this;
    }
    
}

And finally, put this code in ./app/design/frontend/x/x/template/page/template/links_nested.phtml

<?php
/*
* @category    Template
* @package     Sonassi_TemplateLinks
* @copyright   Copyright (c) 2012 Sonassi
*/
?>
<?php
/**
 * @see Mage_Page_Block_Template_Links
 */
?>
<?php $_links = $this->getLinks(); ?>
<?php if(count($_links)>0): ?>
<ul class="links"<?php if($this->getName()): ?> id="<?php echo $this->getName() ?>"<?php endif;?>>
    <?php foreach($_links as $count=>$_link): ?>
        <?php if ($_link instanceof Mage_Core_Block_Abstract):?>
            <?php echo $_link->toHtml() ?>
        <?php else: ?>
            <li<?php if($_link->getIsFirst()||$_link->getIsLast()||$count): ?> class="<?php if($_link->getIsFirst()): ?>first<?php endif; ?><?php if($_link->getIsLast()): ?> last<?php endif; ?> link-<?php echo $count ?>"<?php endif; ?> <?php echo $_link->getLiParams() ?>>
            	<?php echo $_link->getBeforeText() ?><a href="<?php echo $_link->getUrl() ?>" title="<?php echo $_link->getTitle() ?>" <?php echo $_link->getAParams() ?>><?php echo $_link->getLabel() ?></a><?php echo $_link->getAfterText() ?>
            	<?php echo ($_link->getChildMenu()) ? $_link->getChildMenu()->toHtml() : ''; ?>
            </li>
        <?php endif;?>
    <?php endforeach; ?>
</ul>
<?php endif; ?>

Making some nested menus

Now that the extension is in place, you can start creating some nested menus, it follows the same syntax and arguments as the built in addLink() function.

The code can go in any XML file, be it cms.xml, local.xml or page.xml. It is up to you.

So first we create the main menu, the key is to set a new element in addLink called <childMenu/>

<reference name="mymainmenu.links">
    <action method="addLink" translate="label title before_text">
        <label>Information Centre</label>
        <url />
        <title>Information Centre</title>
        <prepare />
        <urlParams />
        <position>100</position>
        <liParams>id="mymainmenu-menu"</liParams>
        <aParams />
        <before_text />
        <after_text />
        <childMenu>mymainmenu.sublinks</childMenu>
    </action>
</reference>

Then we create the submenu,

<block type="page/template_links" name="mymainmenu.sublinks" as="mymainmenuSubLinks">
    <action method="setName">
        <name>mymainmenu-sublinks</name>
    </action>
    <action method="addLink" translate="label title before_text">
        <label>Contact Us</label>
        <url />
        <title>Contact Us</title>
        <prepare />
        <urlParams />
        <position>110</position>
        <liParams />
        <before_text />
        <after_text />
    </action>
    <action method="addLink" translate="label title before_text">
        <label>Shipping and Delivery</label>
        <url />
        <title>Shipping and Delivery</title>
        <prepare />
        <urlParams />
        <position>120</position>
        <liParams />
        <before_text />
        <after_text />
    </action>
</block>

And that is it. You can continue to nest as many levels as you want, just remember to make sure you declare the empty parameters (like aParams or liParams) even if you are not using them – as the core functionality isn’t forgiving if you miss out, or re-sequence the arguments.

  • http://www.developer.web.tr/ Oğuz Çelikdemir

    Dear Benjamin,
    I think you had forgetten where we put last part of the code. Should be available in page.xml, right?

  • sonassi

    @AkeB0N0:disqus The code can go in any XML file, be it cms.xml, local.xml or page.xml. It is up to you.

  • Leprechaun

    Is there a simple way of adding menu items to already existing menu categories, such as ‘Catalog’, ‘System’, etc.?

  • Leandro

    Hi! I missed in the last part… where i put this code?