Programmatically creating a Drupal menu link item

A couple of times recently, I've had to create new links in a Drupal site's menus, but programmatically: that is, automate the adding of another item to an existing menu hierarchy. One option is to export menu items using features, but I've found it occasionally flaky: also, once you've exported the item to a feature and turned the feature on, often the item... still seems to be up for grabs for your next created feature, making it difficult to audit and avoid conflicts.

The easiest thing to do instead - if you're happy with a bit of PHP programming - is to let features export the menu hierarchy container, but then add new menu items using e.g. a hook_install() in the module. There's a complete answer for this on Stack Exchange, but the code snippet is pretty long and involved, and doesn't quite highlight how very simple this is.

So here instead is a module .install file, showing how to create a single menu item:

<?php
 
/**
 * Implements hook_install().
 */
function mymodule_install() {
  // We need to pass this array by reference, so we create it
  // as a local variable first to avoid triggering strict warnings.
  $item = array(
    'link_path' => 'admin',
    'link_title' => 'Link to admin pages',
    'menu_name' => 'main-menu',
    'weight' => 0,
    'expanded' => 0,
  );
 
  // On saving, the $item is passed by reference and hence
  // is given other properties, including the item ID. 
  $item_id = menu_link_save($item);
}

That's it, and the rest is just PHP control flow that you can implement yourself as required. For reference, the only element in that array you need to take care with is menu_name. It must be a "machine name" for one of the existing menu hierarchies. You can obtain a list of existing hierarchy machine names by running the following drush command:

drush sqlq "SELECT menu_name, title FROM menu_custom"

So go forth and multiply your menu items. Oh, and deleting them in the hook_uninstall() is left as an exercise for the reader, so you have been warned.

Comments

Just what I was looking for.  Perfect solution. Thank-you! 

I too am tentative about using Features.  I prefer to use .install and save my configuration in the database.  I encounter I lot fewer issues.

Thanks for the feedback! To clarify, for most things - content types, vocabularies, permissions, roles, Display Suite config etc. - I would definitely rely on features: I don't want to have to keep writing the code to create hundreds of fields (and then deal with my own bugs) when there's a whole community squashing the bugs in features for me. It's just the stuff that's half-content, half-structure (like menu items etc.) where it's not quite perfect.