tags:

views:

1634

answers:

3

I've been reading through the various menu functions in Drupal, but there are sooo many, and I've reached a point of utter confusion and despair... Hoping one of the smarties here can help me out...

Basically, I have four levels to my menu. I'm trying to create a tree that outputs from the second level down.

So, the menu looks like this: LEVEL ONE > Sublevel A > Sublevel I > Sublevel a

I'm trying to output the menu tree beginning with Sublevel A (i.e., Sublevel A > Sublevel I > Sublevel a)

But, can't for the life of me figure out how to do that... I tried simply getting the mlid of the Sublevel A menu (in this case 69), and then

<?php print theme_menu_tree(69); ?>

but it just prints out '69'. Not at all what I expected...

Anyone know how to do this?

+4  A: 

I always wondered why there is no function for this in core, but afaik there is none.

So it looks like we need to roll our own, walking a complete menu tree until we find the subtree we need:

/**
 * Extract a specific subtree from a menu tree based on a menu link id (mlid)
 *
 * @param array $tree
 *   A menu tree data structure as returned by menu_tree_all_data() or menu_tree_page_data()
 * @param int $mlid
 *   The menu link id of the menu entry for which to return the subtree
 * @return array
 *   The found subtree, or NULL if no entry matched the mlid
 */
function yourModule_menu_get_subtree($tree, $mlid) {
  // Check all top level entries
  foreach ($tree as $key => $element) {
    // Is this the entry we are looking for?
    if ($mlid == $element['link']['mlid'])  {
      // Yes, return while keeping the key
      return array($key => $element);
    }
    else {
      // No, recurse to children, if any
      if ($element['below']) {
        $submatch = yourModule_menu_get_subtree($element['below'], $mlid);
        // Found wanted entry within the children?
        if ($submatch) {
          // Yes, return it and stop looking any further
          return $submatch;
        }
      }
    }
  }
  // No match at all
  return NULL;
}

To use it, you first need to get the tree for the whole menu, using menu_tree_page_data() or menu_tree_all_data(), depending on what you need (check the API definitions for the difference). Then you extract the subtree you want, based on the mlid. This subtree can then be rendered into HTML via menu_tree_output():

$mlid = 123; // TODO: Replace with logic to determine wanted mlid
$tree = menu_tree_page_data('navigation'); // TODO: Replace 'navigation' with name of menu you're interested in
// Extract subtree
$subtree = yourModule_menu_get_subtree($tree, $mlid);
// Render as HTML menu list
$submenu = menu_tree_output($subtree);

Disclaimer: I am not sure if this is a good/proper way to do it - it is just the solution I came up with after going through the same procedure as the OP, that is, reading through the whole menu module functions, always wondering if I'm missing the obvious somewhere...

Henrik Opel
When I saw the question earlier today I decided not to answer because the only answer I would have been able to give was along these lines, and seemed to be the wrong way. Now that you posted your answer I don't know if I have to be happy because I came to the same conclusion than you did, or unhappy because there's no better way to do this. Anyhow: +1.
mac
what's the best way to theme the submenu output?
matt ryan
@matt ryan: `menu_tree_output()` already returns a themed menu, so I'm not sure what you mean. If you want to influence the way it creates its output, look at the linked api doc page for the function - it uses `theme_menu_item_link`, `theme_menu_item` and `theme_menu_tree` for this.
Henrik Opel
+5  A: 

The Menu Block module will do exactly what you need. (It uses similar logic to the custom function presented above).

jhedstrom
+1 - good catch. Might well save the OP some coding :)
Henrik Opel
+1 - Nice one indeed! :)
mac
+1  A: 

Still on the path of custom functions... Today - why looking for something totally different - I found yet another colleague facing the same problem and coming up with yet another solution.

The original post is here. The following is the c&p of the code snippet there.

// will return all menu items under "administration".
print theme('menu_tree_by_path','admin');

// will return links to all node submission forms
print theme('menu_tree_by_path','node/add');

// return the correct menu array by path
function menu_get_mid_by_path($path) {
// oddly, menu_get_item accepts a path, but returns the parent id.
  $menu = menu_get_item(null, $path);
  if (isset($menu['children'])) {
// so we have to extract the mid for theme_menu_tree from one of the child items
    if ($pid = end($menu['children'])) {
      $menu = menu_get_item($pid);
      return $menu['pid'];
    }
  }
}

//theme the crap out of it
function theme_menu_tree_by_path($path) {
  if ($mid = menu_get_mid_by_path($path)) {
    return theme('menu_tree', $mid);
  }
}
mac