The menu_links contains the links as they appear in the navigation block (or any other menu block). This is in contrast to the menu_router table which contains information about callbacks.

In general, only put an entry in hook_menu if said entry has different callbacks. Because of this, if you put a loop in there with just the title changing but the same access and page callbacks, it's very likely that you wanted to use menu_link_save instead (maybe with a title callback and/or a load function). This function takes an array, with at least a link_path and a link_title defined. Other possible key-value pairs:

 menu_name   default is navigation
 weight      default is 0
 expanded    whether the item is expanded.
 options     An array of options, @see l for more.
 mlid        Set to an existing value, or 0 or NULL to insert a new link.
 plid        The mlid of the parent.
 router_path The path of the relevant router item.

The menu_links table handling code was designed to be able to handle basically any number of entries. Beware: the user interfaces (book / menu parent chooser and the menu admin overview) in core are not capable of the same, they will be difficult to use above a couple hundred links per menu. On the contrary, the router table is designed to hold as few items as possible.

Another useful function is menu_link_maintain, for a usage example see aggregator_save_category.

Comments

matslats’s picture

This page is talking about the menu_links db table, when I feel it should be talking about menu_link_alter.
Should module developers really be calling menu_link_save, which as far as I can see, is an internal part of the menu_rebuild process.

Also valuable would be a summary of the menu building process and where hook_menu, hook_menu_alter, and hook_menu_link_alter fit into this process.

The menu system consists of two main db tables, the router and the links. The menu router stores links, callbacks, access control, title callbacks and the menu tree. The links table includes only the menu items themselves, with more front-end oriented data, some duplicated from the router for ease of access.
When menu rebuild is called it first assimilates all the $items in all the hook_menu functions. Then it gives modules an opportunity to change menu items generated by other modules in hook_menu_alter, then it stores the whole lot in the menu_router table.

The router table is then passed to another function which extracts only the links (i.e. not the callbacks) and stores them in the menu_link table. Then it calls hook_menu_link_alter giving all modules an opportunity to do things like hide, style or reword menu items.

How accurate is this?

But I'm still not clear: when should developers menu_link_save, menu_link_maintain, and hook_menu_alter?`

mattyoung’s picture

The router table is then passed to another function which extracts only the links (i.e. not the callbacks) and stores them in the menu_link table. Then it calls hook_menu_link_alter giving all modules an opportunity to do things like hide, style or reword menu items.

Exactly what menu item types can be altered with hook_menu_link_alter()? It appears only MENU_NORMAL_ITEM can be altered and MENU_DEFAULT_LOCAL_TASK and MENU_LOCAL_TASK menu items cannot be altered this way. If this is the case, I think the api is bad bacause LOCAL_TASK item types are part of the front end ui and should be available to hook_menu_link_alter().

My use case is I want to alter some LOCAL_TASK menu item link_path define by another module to append query string ?destination=xzy (so when the LOCAL_TASK request is complete, user is returned back to page 'xyz'). I was hoping to use hook_menu_link_alter() to set items['options']['alter'] = TRUE. And in hook_translated_menu_link_alter(), do items['localized_options']['query'] = drupal_get_destination();.

dafeder’s picture

mattyoung, I am trying to do something very similar, pass a query string to link in a menu_local_task tab. Did you ever find a solution? Thanks,

markj’s picture

Beware: the user interfaces (book / menu parent chooser and the menu admin overview) in core are not capable of the same, they won't really work above a couple hundred links per menu.

Does this mean that if I populate a book with more than a couple hundred pages programmatically (each with a row in menu_link), users won't be able to manage that book with the UI?

markj’s picture

This is a great resource for technical details about the D6 menu system: http://szeged2008.drupalcon.org/files/menu-szeged2008.ppt