Magento: Neu in Version 1.7: das Top Menü

Es gibt einen neuen Block in Magento 1.7 und jeder, der das Navigationsmenü in älteren Versionen überschrieben hat, wird seine Änderungen in der Magento 1.7 nicht mehr wiederfinden.

In Magento 1.6 war diese Klasse zuständig für die Navigationsaufbereitung:
Mage_Catalog_Block_Navigation
und der funktionierende Rewrite war dieser:

<global>
   <blocks>
       <catalog>
        <rewrite>
            <navigation>MeinNameSpace_MeinModul_Block_Navigation</navigation>
        </rewrite>
    </catalog>
    </blocks>
</global>


In der neuen Magento Version hat man sich also von dieser Klasse verabschiedet. Die Navigation ist nun in einem völlig neuen Block untergebracht: dem “Topmenu” innerhalb des Page-Moduls.

In der page.xml wird man also auch fündig:

<block type="page/html_header" name="header" as="header">
    <block type="page/template_links" name="top.links" as="topLinks"/>
    <block type="page/switch" name="store_language" as="store_language" 
         template="page/switch/languages.phtml"/>
    <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>
    <block type="page/html_wrapper" name="top.container" as="topContainer" translate="label">
        <label>Page Header</label>
        <action method="setElementClass"><value>top-container</value></action>
    </block>
</block>


Die neue Zuständigkeit für das Rendern der Navigation hat also die Klasse
Mage_Page_Block_Html_Topmenu
übernommen.

Und in dieser Klasse scheint es neue Events zu geben:
page_block_html_topmenu_gethtml_before
und
page_block_html_topmenu_gethtml_after
(welche bei Google noch 0 Treffer liefern, was ich hiermit ändern möchte!)

Der _after Event-Observer liefert das fertige Menu und den fertigen HTML-Code der Navigation.

Wie man mit dem _before Event das Menu beeinflusst, möchte ich kurz zeigen.

Erstmal Magento mitteilen, dass man diesen Event gerne nutzen möchte:

<frontend>
     <events>
            <page_block_html_topmenu_gethtml_before>
                <observers>
                    <meinnamespace_page_block_html_topmenu_gethtml_before>
                        <type>singleton</type>
                        <class>meinnamespace_meinmodulname/observer</class>
                        <method>topmenuGethtmlBefore</method>
                    </meinnamespace_page_block_html_topmenu_gethtml_before>
                </observers>
            </page_block_html_topmenu_gethtml_before>
        </events>
</frontend>


Dann die Observer-Klasse mit der observierenden Funktion anlegen:

class MeinNameSpace_MeinModul_Model_Observer { 
   public function topmenuGethtmlBefore (Varien_Event_Observer $observer) {
        /** @var $event Varien_Event */
        $event = $observer->getEvent();
        
        ....
   }
}


Was im Event-Objekt nun zur Verfügung steht, kann man in der Mage_Page_Block_Html_Topmenu Klasse erfahren:

public function getHtml($outermostClass = '', $childrenWrapClass = '')
{
    Mage::dispatchEvent('page_block_html_topmenu_gethtml_before', array(
        'menu' => $this->_menu
    ));

    $this->_menu->setOutermostClass($outermostClass);
    $this->_menu->setChildrenWrapClass($childrenWrapClass);

    $html = $this->_getHtml($this->_menu, $childrenWrapClass);

    Mage::dispatchEvent('page_block_html_topmenu_gethtml_after', array(
        'menu' => $this->_menu,
        'html' => $html
    ));

    return $html;
}


Nutzt man also den page_block_html_topmenu_gethtml_before Event, so steht einem mittels

/** @var $menu Varien_Data_Tree_Node */
$menu = $event->getMenu();


das Objekt zur Verfügung, das die Navigationselemente enthält.

(bei page_block_html_topmenu_gethtml_after stünde einem noch der generierte HMTL-String des Menüs zur Verfügung mittels: $html = $event->getHtml(); )

Nun kann man das Menü manipulieren.

Man kann beispielsweise den Namen eines Menüpunkts ändern:

public function topmenuGethtmlBefore (Varien_Event_Observer $observer) {
        /** @var $event Varien_Event */
        $event = $observer->getEvent();

        /** @var $menu Varien_Data_Tree_Node */
        $menu = $event->getMenu();

        /** @var $menuCollection Varien_Data_Tree_Node_Collection */
        $menuCollection = $menu->getChildren();

        /** @var $item Varien_Data_Tree_Node */
        foreach ($menuCollection as $item) {
            if ($item->getName() == 'Alter Name') { 
                $item->setName('Neuer Name');
            }
        }
    }
}


Oder man kann Menüpunkte entfernen, beispielsweise alle Unterkategorien einer Kategorie:

public function topmenuGethtmlBefore (Varien_Event_Observer $observer) {
        /** @var $event Varien_Event */
        $event = $observer->getEvent();

        /** @var $menu Varien_Data_Tree_Node */
        $menu = $event->getMenu();

        /** @var $menuCollection Varien_Data_Tree_Node_Collection */
        $menuCollection = $menu->getChildren();

        /** @var $item Varien_Data_Tree_Node */
        foreach ($menuCollection as $item) {
            if ($item->getName() == 'Alter Name') {
            
                $item->setName('Neuer Name');
                
                /** @var $childNode Varien_Data_Tree_Node */
                foreach ($item->getAllChildNodes() as $childNode) {
                    $item->removeChild($childNode);
                }
            }
        }
    }
}


Man kann natürlich hier auch Menüpunkte hinzufügen. Und verschieben. Und was sonst noch, muss ich noch herausfinden. Aber dafür ist Magento ja da.