Magentoをカスタマイズしていると、色々な課題に直面します。
その一つがカテゴリメニューのカスタマイズです。

今回はMagento 1.9系から導入された、「topmenu renderer」を使って、独自のメニュー描画処理を行う方法を解説します。

Magentoのカテゴリメニュー描画処理

Magentoのカテゴリメニューは、Mage_Page_Block_Html_Topmenuを用いて描画されています。
左ナビにカテゴリナビを出すようなエクステンションは、このクラスをリライトすることによって、異なるHTML出力を実現していました。

Magento 1.9までの悩み

ただ、この仕組には基本的に悩ましい問題がありました。
それは、BlockクラスにHTMLパーツがハードコードされているため、カスタマイズしにくいという問題です。
テーマ別にマークアップを調整したいと思っても、基本的な処理はBlockクラスに書き込まれているため、テーマ別に実装することができません。
(もちろん独自にカスタマイズすればいいのですが、たいていのサイトではそうなっていません)

つまり、メニューのマークアップカスタマイズは面倒なカスタマイズだったわけです。

Magento 1.9で導入された「topmenu renderer」

この問題を解決するために、1.9以降では、「topmenu renderer」という仕組が導入されました。
1.9系のデフォルトテーマである、RWDテーマのメニュー定義を見ると以下のように書かれています。

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

また、Mage_Page_Block_Html_TopmenuのgetHtml()にも以下のように書かれていて、所定の定義を書くことで、Mage_Page_Block_Html_Topmenu_Rendererに描画処理を委ねることができます。

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

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

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

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

        return $html;
    }

topmenu rendererは、template/page/html/topmenu/renderer.phtmlを使ってカテゴリメニューを描画します。
1.9以降の場合はこのテンプレートをテーマごとにカスタマイズすることで、独自のメニュー表示を実現することができます。

2つのテンプレートを使って別々のメニューを描画する

さて、このtopmenu rendererを応用することで、別々のカテゴリメニューを描画することができます。

例えばレスポンシブテーマを作る際に、PC表示のメニューとスマホ表示のメニューを別にしたい場合、以下のようにレイアウトXMLを定義することで実現できます。

<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>
<block type="core/text_list" name="top.menu.sp" as="topMenuSp" translate="label">
<label>SP Navigation Bar</label>
<block type="page/html_topmenu" name="catalog.topnav.sp" template="page/html/topmenu_sp.phtml">
<block type="page/html_topmenu_renderer" name="catalog.topnav.renderer" template="page/html/topmenu/renderer_sp.phtml"></block>
</block>
</block>

このように定義すると、それぞれ別のテンプレートを使用するため、全く違った構造のテーマを表示することができます。 
(ただ、パフォーマンスの面ではあまりおすすめできるものではないので、できれば1つのメニューで完結させたいところです)

また、テンプレート内に静的ブロックを表示することもできます。アイデア次第で様々なカスタマイズができるでしょう。