Magento: How To add category Description in topmenu?

1.5k views Asked by At

I'd like to add the categories description into the topmenu navigation on Magento.

I've tried a hack using CMS block but it didn't work properly (it shows category but outside of topmenu) Do you have any any clue about how doing this easily?

Thanks for your help.

2

There are 2 answers

0
Malachy On

Quick summary: (Magento 1.8.2.0 and higher) (see last paragraph for earlier versions)

Add a child block under the top.menu block in your theme's local.xml and create (or copy from RWD theme) a custom renderer.phtml file to generate your custom menu HTML that includeds category descriptions.

The recommended path for renderer.phtml is app/design/frontend/yourpackage/default/template/page/html/topmenu/renderer.phtml.

Detail:

If you have Magento 1.8.2.0 or above you should seek to implement a menu renderer.phtml file due to this code:

file: app/Mage/Page/Block/Html/Topmenu.php
class: Mage_Page_Block_Html_Topmenu extends Mage_Core_Block_Template
function: getHtml()

    public function getHtml($outermostClass = '', $childrenWrapClass = '')
    {
    //...

        if ($renderer = $this->getChild('catalog.topnav.renderer')) {
            $renderer->setMenuTree($this->_menu)->setChildrenWrapClass($childrenWrapClass);
            $html = $renderer->toHtml();
        } else {
            $html = $this->_getHtml($this->_menu, $childrenWrapClass);
        }
    //...
    }

You can see here that if there is a child block named catalog.topnav.renderer then Magento will use it, otherwise it falls back gracefully to use $this->_getHtml() where $this is Mage_Page_Block_Html_Topmenu.

Unfortunately the Magento default theme does not use the new renderer feature so there is not an example in the base theme. However the most excellent RWD theme which comes as standard with Magento does use the menu renderer and I highly recommend you study the RWD theme code to learn how to use a menu renderer phtml file.

Specifically you should create an additional entry in your local.xml to define your menu renderer:

file: app/design/frontend/yourpackage/default/layout/local.xml

            <block type="page/html_header" name="header" as="header">
                <block type="core/text_list" name="top.menu" as="topMenu" translate="label">
                    <label>Navigation Bar</label>
                    <block type="page/html_topmenu" name="catalog.topnav" template="page/html/topmenu.phtml">
                        <block type="page/html_topmenu_renderer" name="catalog.topnav.renderer" template="page/html/topmenu/renderer.phtml"/>
                    </block>
                </block>
            </block>

Or something like that to suit your theme layout. Noting specifically the all-important hard coded child block name name="catalog.topnav.renderer"

And then I would start with a copy of the RWD renderer.phml file copied into your theme path page/html/topmenu/renderer.phtml

file: app/design/frontend/rwd/default/template/page/html/topmenu/renderer.phtml

<?php
/** @var Mage_Page_Block_Html_Topmenu_Renderer $this */
/** @var Varien_Data_Tree_Node $menuTree */
/** @var string $childrenWrapClass */

$html = '';

$children = $menuTree->getChildren();
$parentLevel = $menuTree->getLevel();
$childLevel = is_null($parentLevel) ? 0 : $parentLevel + 1;

$counter = 1;
$childrenCount = $children->count();

$parentPositionClass = $menuTree->getPositionClass();
$itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-';

foreach ($children as $child) {
    $child->setLevel($childLevel);
    $child->setIsFirst($counter == 1);
    $child->setIsLast($counter == $childrenCount);
    $child->setPositionClass($itemPositionClassPrefix . $counter);

    $outermostClassCode = 'level'. $childLevel;
    $_hasChildren = ($child->hasChildren()) ? 'has-children' : '';

    $html .= '<li '. $this->_getRenderedMenuItemAttributes($child) .'>';

    $html .= '<a href="'. $child->getUrl() .'" class="'. $outermostClassCode .' '. $_hasChildren .'">'. $this->escapeHtml($this->__($child->getName())) .'</a>';

    if (!empty($childrenWrapClass)) {
        $html .= '<div class="'. $childrenWrapClass .'">';
    }

    $nextChildLevel = $childLevel + 1;

    if (!empty($_hasChildren)) {
        $html .= '<ul class="level'. $childLevel .'">';
        $html .=     '<li class="level'. $nextChildLevel .' view-all">';
        $html .=         '<a class="level'. $nextChildLevel .'" href="'. $child->getUrl() .'">';
        $html .=             $this->__('View All') . ' ' . $this->escapeHtml($this->__($child->getName()));
        $html .=         '</a>';
        $html .=     '</li>';
        $html .=     $this->render($child, $childrenWrapClass); //THIS IS THE RECURSION
        $html .= '</ul>';
    }

    if (!empty($childrenWrapClass)) {
        $html .= '</div>';
    }

    $html .= '</li>';

    $counter++;
}

return $html;

And by studying that file you can start to see where and how various modifications affect the menu html.

Please note the code that Mage_Page_Block_Html_Topmenu_Renderer::render() uses to handle your rendrerer.phtml file: unusually for Magento it is a direct include $this->_templateFile and either returns the string or the ob_get_cleaned buffer:

file: /app/code/core/Mage/Page/Block/Html/Topmenu/Renderer.php
class: Mage_Page_Block_Html_Topmenu_Renderer
function: render()

    public function render(Varien_Data_Tree_Node $menuTree, $childrenWrapClass)
    {
        ob_start();
        $html = include $this->_templateFile;
        $directOutput = ob_get_clean();

        if (is_string($html)) {
            return $html;
        } else {
            return $directOutput;
        }
    }

If you are using a version prioir to 1.8.2.0 you will need to rewrite the class Mage_Page_Block_Html_Topmenu and override its function _getHtml() to inject your extra HTML into the menu. The main disadvantage being you need to recompile every time the menu layout changes.

0
M4tth On

Thank you for this answer.
Now It's Ok I display the topmenu from my template.
The thing that I don't really understand is how to get the description value.
Does the $children = $menuTree->getChildren(); object contain the description ?
I've tried to call it with $child->getDescription() but it did not work.