Adding Drupal Menus to Magento

Drupal is great. Magento is great. However they are great for very different reasons. I'm currently working on integrating an existing Magento store with a new Drupal frontend to get better (any) content handling. In my case the store is pretty much self contained and doesn't require much from Drupal, other than the primary-links menu.

As it turns out, getting the menu structure from Drupal and displaying it inside Magento is actually quite simple. All you need on the Drupal side is the Services module and to turn on XMLRPC and the "menu" service. For Magento, I needed a custom module to consume that service.

As it turns out, that was rather simple as well. I created a new module provides a custom block.

<!--
  Module config file.
  File: /app/code/local/Namespace/Drupal/etc/config.php
-->
<?xml version="1.0" encoding="UTF-8"?>
 
<config>
    <modules>
        <Namespace_Drupal>
            <version>0.1.0</version>
        </Namespace_Drupal>
    </modules>
    <frontend>
        <layout>
            <updates>
                <drupal module="Namespace_Drupal">
                    <file>namespace_drupal.xml</file>
                </drupal>
            </updates>
        </layout>
    </frontend>
    <global>
        <blocks>
            <drupal><class>Namespace_Drupal_Block</class></drupal>
        </blocks>
    </global>
</config>
/*********************************************************
  This is the Magento block flle.  It gets called by configuring your layout
  to add a new block to your skin.
 
  This should be part of your local module.
  File: /app/code/local/Namespace/Drupal/Block/Navigation.php
**********************************************************/
class Namespace_Drupal_Block_Navigation extends Mage_Core_Block_Template
{
 
    protected $_menuName = '';
 
    protected function _construct()
    {
        $this->addData(array(
            'cache_lifetime'    => 300, // set cache to 5 minutes
            'cache_tags'        => array('drupal_menu')
        ));
    }
 
    /**
     * Set the drupal menu to grab
     *
     * @param  string $menuName
     * @return Pro_Drupal_Block_Navigation
     */
    public function setMenu($menuName = '') {
        $this->_menuName = $menuName;
        return $this;
    }
 
    /**
     * Get the full html for the specified menu
     *
     * @return string
     */
    public function getMenu()
    {
        $xmlRpcUrl =  'path_to_drupal/services/xmlrpc';
 
        try {
            $client = new Zend_XmlRpc_Client($xmlRpcUrl);
            $menuArray = $client->call('menu.get', array($this->_menuName, array(
                'depth', 'title', 'menu_name', 'expanded', 'hidden', 'has_children', 'children', 'link_path', 'href', 'path_alias'
            )));
 
            $html = '';
            foreach($menuArray as $menu) {
                $html .= $this->_drawItem($menu); //$html .= $menu['title'] . ' ';
            }
        } catch(Exception $e) {
            return 'Path: ' . $xmlRpcUrl . ': ' . $e->getMessage();
        }
 
        return '<ul>' . PHP_EOL . $html . '</ul>' . PHP_EOL;
    }
 
    /**
     * Generate the html for the current menu level
     *
     * @param array $menu
     * @return string
     */
    protected function _drawItem($menu)
    {
        // bring the menu array elements into
        // local space as variables
        extract($menu);
 
        $drupalBase = '/';
 
        $indent = str_pad('', $depth * 4);
 
        if($hidden == 1) {
            return;
        }
 
        if($path_alias == '<front>') {
            $path_alias = '';
        } 
 
        if(substr($path_alias, 0, 4) != 'http') {
            $path_alias = $drupalBase . $path_alias;
        }
 
        $html = $indent . '<li';
        if ($has_children) {
             $html.= ' onmouseover="toggleMenu(this,1)" onmouseout="toggleMenu(this,0)"';
        }
 
        $html.= ' class="level'.$depth;
        $html.= ' drupal-nav-'.$menu_name;
        if (0) {
            $html .= ' last';
        }
        if ($has_children) {
            $html .= ' parent';
        }
        $html.= '">'. PHP_EOL;
        $html.= $indent . '    <a href="'.$path_alias.'"><span>'.$title.'</span></a>'. PHP_EOL;
 
        if ($has_children){
 
            $j = 0;
            $htmlChildren = '';
            foreach ($children as $child) {
                $htmlChildren.= $this->_drawItem($child);
            }
 
            if (!empty($htmlChildren)) {
                $html.= $indent . '    <ul class="level' . $depth . '">'. PHP_EOL . 
                        $htmlChildren .
                        $indent . '    </ul>' . PHP_EOL;
            }
 
        }
        $html.= $indent . '</li>' . PHP_EOL;
        return $html;
 
    }
 
}

The last files you need to get this going is a layout and template file. This will configure Magento to insert the Drupal menu in the top.menu block, before the category.topnav menu. It also can define the menu you want to grab from Drupal, so you could use this in multiple places within your Magento layout to call different Drupal menus.

<!--
  File is defined in the config.xml
  File: /app/design/frontend/default/default/namespace_drupal.xml
-->
<layout version="0.1.0">
    <default>
        <reference name="top.menu">
            <block type="drupal/navigation" name="drupal.topnav" template="namespace/drupal/navigation/top.phtml" before="category.topnav">
                <action method="setMenu"><name>primary-links</name></action>
            </block>
        </reference>
    </default>
</layout>

The template file is minimal.

File: namespace/drupal/navigation/top.phtml
<div class="drupal-menu">
<?php echo $this->getMenu(); ?>
</div>

As a note, I haven't tested this exact code since I've cut out some application specific code from a working example. Also note that I don't take into account for any security on the Drupal Services module here.