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
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'];
}
?> Login or register to post comments
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.phpCall menu_rebuild
If your newly created links aren't showing up, call menu_rebuild().
Additional Parameters
Another Parameter that I've found helpful is
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.