views:

1037

answers:

2

In implementing hook_menu for a module, I am trying to put some items into a submenu.

So far I have something like this

$items['MyModule'] = array(
  //...
  'page callback' => 'system_admin_menu_block_page',
  'file' => 'system.admin.inc',
  'file path' => drupal_get_path('module','system'),
);

$items['MyModule/MenuItem1'] = array(
  //...
);

$items['MyModule/SubMenu'] = array(
  //...
  'page callback' => 'system_admin_menu_block_page',
  'file' => 'system.admin.inc',
  'file path' => drupal_get_path('module','system'),
);

$items['MyModule/SubMenu/SubMenuItem1'] = array(
  //...
);

I expect the SubMenu to appear as, well, a submenu to the MyModule menu, and for the SubMenuItems to appear under that submenu. This is the default behaviour described at the Drupal API documentation.

  • MyModule
    • MenuItem1
    • SubMenu
      • SubMenuItem1

However, all items appear under the MyModule menu.

  • MyModule
    • MenuItem1
    • SubMenuItem1
    • SubMenu

What am I doing wrong?

*EDIT: A typo (which I have fixed) caused SubMenu to be a separate element rather than a child element of MyModule. I still don't understand why SubMenuItem1 does not render under the SubMenu, though.

A: 

hook_menu isn't really the place to set weighting, I'm sure it can be done there but you'll find that just creating a normal menu item and dragging it into place in the admin's Menus dialogue will save you a ton of hassle and pain.

The reason, as I understand it, is that menu hierarchy is determined in part through a weighting system rather than by the path you set. Convention certainly dictates how people set their path, but just making a normal menu item at admin/monkey does not put a monkey item into the admin menus automagically.

Chuck Vose
While not absolutely essential, I would like to structure the module's menu within the module's code, so that others can install it and see a helpful default menu hierarchy.
Dan
+3  A: 

I can not reproduce your problem - using your menu hierarchy, all entries appear under the navigation menu in the expected order and nesting.

Have you (re)tried from a clean state (that is, with your module uninstalled and the menu entries gone)? To explain why I ask this, I have to elaborate a bit:

Drupal 6 split the menu definition storage in two tables. There is the menu_router table, which stores the path<>callback relations defined via hook_menu(). This does not define any 'real' menu entry (as in menu menu, e.g. the navigation menu). It does only define the Drupal internal menu structure, which has nothing to do with the displayed menus, but only with the internal hierarchy of mapping paths to callback functions!

Then there is the menu_links table, which stores the 'real' menu entries as they appear under the various displayable menus (e.g. navigation, primary links, etc.). The entries there also define the nesting order by storing a 'parent menu id' (plid) for each entry, pointing to the parent entry, or 0 for a top level entry.

Now whenever you define path/callback combinations via hook_menu(), Drupal just puts that entry into the menu_router table. If you define them as MENU_NORMAL_ITEM or MENU_SUGGESTED_ITEM, Drupal will additionally try to create an entry in the menu_links table. If an entry for that path already exists, Drupal will not alter its placement in the hierarchy, as it assumes that a user moved it on purpose. You should think of this menu_link entry creation by hook_menu() as a convenience add on that can save you the trouble off explicitly adding them via the functions mentioned below, but the mechanism is not very flexible and tries not to interfere with existing configurations (otherwise a manually edited menu would constantly get reordered on every rebuilding of the menu cache).

So you should try again while making sure that none of your paths have an existing entry in the `menu_links' table.

For your goal of providing a proper default menu on install of your module (and for more control over whats happening), you should take a look into the menu_link_save() and menu_link_maintain() functions. You might also want to read When and how to use menu_links.

Henrik Opel
+1 "If an entry for that path already exists, Drupal will not alter its placement in the hierarchy, as it assumes that a user moved it on purpose." I think that is exactly what was happening. Reinstalling the module revealed that it did, in fact, create the desired default menu display. Thanks for the useful info and helpful links.
Dan

related questions