Community Documentation

menu_link_save

6 menu.inc menu_link_save(&$item)
7 menu.inc menu_link_save(&$item, $existing_item = array(), $parent_candidates = array())
8 menu.inc menu_link_save(&$item, $existing_item = array(), $parent_candidates = array())

Save a menu link.

Parameters

$item: An array representing a menu link item. The only mandatory keys are link_path and link_title. Possible keys are:

  • 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.

Return value

The mlid of the saved menu link, or FALSE if the menu link could not be saved.

Related topics

▾ 13 functions call menu_link_save()

book_admin_edit_submit in modules/book/book.admin.inc
Handle submission of the book administrative page form.
book_update_6000 in modules/book/book.install
Drupal 5.x to 6.x update.
menu_edit_item_submit in modules/menu/menu.admin.inc
Process menu and menu item add/edit form submissions.
menu_edit_menu_submit in modules/menu/menu.admin.inc
Submit function for adding or editing a custom menu.
menu_enable in modules/menu/menu.module
Implementation of hook_enable()
menu_link_maintain in includes/menu.inc
Insert, update or delete an uncustomized menu link related to a module.
menu_nodeapi in modules/menu/menu.module
Implementation of hook_nodeapi().
menu_overview_form_submit in modules/menu/menu.admin.inc
Submit handler for the menu overview form.
menu_reset_item in modules/menu/menu.module
Reset a system-defined menu item.
system_update_6021 in modules/system/system.install
Migrate the menu items from the old menu system to the new menu_links table.
_book_update_outline in modules/book/book.module
Common helper function to handles additions and updates to the book outline.
_menu_delete_item in includes/menu.inc
Helper function for menu_link_delete; deletes a single menu link.
_menu_navigation_links_rebuild in includes/menu.inc
Helper function to build menu links for the items in the menu router.

File

includes/menu.inc, line 1904
API for the Drupal menu system.

Code

<?php
function menu_link_save(&$item) {

  // Get the router if it's already in memory. $menu will be NULL, unless this
  // is during a menu rebuild
  $menu = _menu_router_cache();
  drupal_alter('menu_link', $item, $menu);

  // This is the easiest way to handle the unique internal path '<front>',
  // since a path marked as external does not need to match a router path.
  $item['_external'] = menu_path_is_external($item['link_path'])  || $item['link_path'] == '<front>';
  // Load defaults.
  $item += array(
    'menu_name' => 'navigation', 
    'weight' => 0, 
    'link_title' => '', 
    'hidden' => 0, 
    'has_children' => 0, 
    'expanded' => 0, 
    'options' => array(), 
    'module' => 'menu', 
    'customized' => 0, 
    'updated' => 0,
  );
  $existing_item = FALSE;
  if (isset($item['mlid'])) {
    $existing_item = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE mlid = %d", $item['mlid']));
  }

  if (isset($item['plid'])) {
    $parent = db_fetch_array(db_query("SELECT * FROM {menu_links} WHERE mlid = %d", $item['plid']));
  }
  else {
    // Find the parent - it must be unique.
    $parent_path = $item['link_path'];
    $where = "WHERE link_path = '%s'";
    // Only links derived from router items should have module == 'system', and
    // we want to find the parent even if it's in a different menu.
    if ($item['module'] == 'system') {
      $where .= " AND module = '%s'";
      $arg2 = 'system';
    }
    else {
      // If not derived from a router item, we respect the specified menu name.
      $where .= " AND menu_name = '%s'";
      $arg2 = $item['menu_name'];
    }
    do {
      $parent = FALSE;
      $parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
      $result = db_query("SELECT COUNT(*) FROM {menu_links} " . $where, $parent_path, $arg2);
      // Only valid if we get a unique result.
      if (db_result($result) == 1) {
        $parent = db_fetch_array(db_query("SELECT * FROM {menu_links} " . $where, $parent_path, $arg2));
      }
    } while ($parent === FALSE && $parent_path);
  }
  if ($parent !== FALSE) {
    $item['menu_name'] = $parent['menu_name'];
  }
  $menu_name = $item['menu_name'];
  // Menu callbacks need to be in the links table for breadcrumbs, but can't
  // be parents if they are generated directly from a router item.
  if (empty($parent['mlid']) || $parent['hidden'] < 0) {
    $item['plid'] =  0;
  }
  else {
    $item['plid'] = $parent['mlid'];
  }

  if (!$existing_item) {
    db_query("INSERT INTO {menu_links} (
       menu_name, plid, link_path,
      hidden, external, has_children,
      expanded, weight,
      module, link_title, options,
      customized, updated) VALUES (
      '%s', %d, '%s',
      %d, %d, %d,
      %d, %d,
      '%s', '%s', '%s', %d, %d)", 
      $item['menu_name'], $item['plid'], $item['link_path'], 
      $item['hidden'], $item['_external'], $item['has_children'], 
      $item['expanded'], $item['weight'], 
      $item['module'],   $item['link_title'], serialize($item['options']), 
      $item['customized'], $item['updated']);
    $item['mlid'] = db_last_insert_id('menu_links', 'mlid');
  }

  if (!$item['plid']) {
    $item['p1'] = $item['mlid'];
    for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) {
      $item["p$i"] = 0;
    }
    $item['depth'] = 1;
  }
  else {
    // Cannot add beyond the maximum depth.
    if ($item['has_children'] && $existing_item) {
      $limit = MENU_MAX_DEPTH - menu_link_children_relative_depth($existing_item) - 1;
    }
    else {
      $limit = MENU_MAX_DEPTH - 1;
    }
    if ($parent['depth'] > $limit) {
      return FALSE;
    }
    $item['depth'] = $parent['depth'] + 1;
    _menu_link_parents_set($item, $parent);
  }
  // Need to check both plid and menu_name, since plid can be 0 in any menu.
  if ($existing_item && ($item['plid'] != $existing_item['plid'] || $menu_name != $existing_item['menu_name'])) {
    _menu_link_move_children($item, $existing_item);
  }
  // Find the callback. During the menu update we store empty paths to be
  // fixed later, so we skip this.
  if (!isset($_SESSION['system_update_6021']) && (empty($item['router_path'])  || !$existing_item || ($existing_item['link_path'] != $item['link_path']))) {
    if ($item['_external']) {
      $item['router_path'] = '';
    }
    else {
      // Find the router path which will serve this path.
      $item['parts'] = explode('/', $item['link_path'], MENU_MAX_PARTS);
      $item['router_path'] = _menu_find_router_path($item['link_path']);
    }
  }
  db_query("UPDATE {menu_links} SET menu_name = '%s', plid = %d, link_path = '%s',
    router_path = '%s', hidden = %d, external = %d, has_children = %d,
    expanded = %d, weight = %d, depth = %d,
    p1 = %d, p2 = %d, p3 = %d, p4 = %d, p5 = %d, p6 = %d, p7 = %d, p8 = %d, p9 = %d,
    module = '%s', link_title = '%s', options = '%s', customized = %d WHERE mlid = %d", 
    $item['menu_name'], $item['plid'], $item['link_path'], 
    $item['router_path'], $item['hidden'], $item['_external'], $item['has_children'], 
    $item['expanded'], $item['weight'],   $item['depth'], 
    $item['p1'], $item['p2'], $item['p3'], $item['p4'], $item['p5'], $item['p6'], $item['p7'], $item['p8'], $item['p9'], 
    $item['module'],   $item['link_title'], serialize($item['options']), $item['customized'], $item['mlid']);
  // Check the has_children status of the parent.
  _menu_update_parental_status($item);
  menu_cache_clear($menu_name);
  if ($existing_item && $menu_name != $existing_item['menu_name']) {
    menu_cache_clear($existing_item['menu_name']);
  }

  _menu_clear_page_cache();
  return $item['mlid'];
}
?>

Comments

Create menu & add items to it

<?php
module_load_include
('inc', 'menu', 'menu.admin');

$form_state = array();
$form_state['values'] = array(
 
'menu_name'     => 'test-menu',
 
'title'         => 'Test Menu Title',
 
'description'   => 'Test Menu Description',
);
drupal_execute('menu_edit_menu', $form_state, NULL);

$form_state = array();
$form_state['values']['menu'] = array(
 
'link_path'     => '<front>',
 
'link_title'    => 'test a',
 
'description'   => 'test b',
 
'enabled'       => 1,
 
'expanded'      => 0,
 
'parent'        => 'menu-test-menu:0',
 
'weight'        => 0,
 
'customized'    => 1,
);
drupal_execute('menu_edit_item', $form_state, 'add', NULL, array('menu_name' => 'menu-test-menu'));

$form_state = array();
$form_state['values']['menu'] = array(
 
'link_path'     => '<front>',
 
'link_title'    => 'test y',
 
'description'   => 'test z',
 
'enabled'       => 1,
 
'expanded'      => 0,
 
'parent'        => 'menu-test-menu:0',
 
'weight'        => -1,
 
'customized'    => 1,
);
drupal_execute('menu_edit_item', $form_state, 'add', NULL, array('menu_name' => 'menu-test-menu'));
?>

If you are using drush php-script to run this code...

Make sure you specify to run the code as uid = 1. It will not work if you do not since drush runs as anonymous.

drush -u 1 scr create_menu_code.php

Call menu_rebuild

If your newly created links aren't showing up, call menu_rebuild().

Additional Parameters

Another Parameter that I've found helpful is

  • module: Set to the name of the module you are calling this function from.

This is very helpful if you are running this function in hook_install. You can then remove these menu links in hook_uninstall like this:
db_query("DELETE FROM {menu_links} WHERE module = 'mymodule'");

There is an error in the

There is an error in the documentation, with the params...
where it says router_path, it must say link_path, almost in drupal 6.22
Also note that you can set the link_title as array key in order to set the title of the menu item

Remember to use

Remember to use menu_link_save() with the internal Drupal path. If the url alias is already used, you can run the following code to update your menu :

$result = db_queryd("SELECT mlid FROM {menu_links} WHERE menu_name = '%s'", 'menu-to-update');
while ($m = db_fetch_array($result)) {
$link = menu_link_load($m['mlid']);
$link['link_path'] = drupal_get_normal_path($link['link_path']);
menu_link_save($link);
}

Helpful for me.

Login or register to post comments