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.
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 viahook_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 themenu_router
table. If you define them asMENU_NORMAL_ITEM
orMENU_SUGGESTED_ITEM
, Drupal will additionally try to create an entry in themenu_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 thismenu_link
entry creation byhook_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()
andmenu_link_maintain()
functions. You might also want to read When and how to use menu_links.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.