| 7 menu.inc | menu_link_save(&$item, |
| 6 menu.inc | menu_link_save(& |
| 8 menu_link.module | menu_link_save(MenuLink $menu_link) |
Saves a menu link.
After calling this function, rebuild the menu cache using menu_cache_clear_all().
Parameters
$item: An associative array representing a menu link item, with elements:
- link_path: (required) The path of the menu item, which should be normalized first by calling drupal_get_normal_path() on it.
- link_title: (required) Title to appear in menu for the link.
- menu_name: (optional) The machine name of the menu for the link. Defaults to 'navigation'.
- weight: (optional) Integer to determine position in menu. Default is 0.
- expanded: (optional) Boolean that determines if the item is expanded.
- options: (optional) An array of options, see l() for more.
- mlid: (optional) Menu link identifier, the primary integer key for each menu link. Can be set to an existing value, or to 0 or NULL to insert a new link.
- plid: (optional) The mlid of the parent.
- router_path: (optional) The path of the relevant router item.
$existing_item: Optional, the current record from the {menu_links} table as an array.
$parent_candidates: Optional array of menu links keyed by mlid. Used by _menu_navigation_links_rebuild() only.
Return value
The mlid of the saved menu link, or FALSE if the menu link could not be saved.
Related topics
23 calls to menu_link_save()
- book_admin_edit_submit in modules/
book/ book.admin.inc - Form submission handler for book_admin_edit().
- MenuLinksUnitTestCase::assertMenuLinkParents in modules/
simpletest/ tests/ menu.test - Assert that at set of links is properly parented.
- MenuLinksUnitTestCase::createLinkHierarchy in modules/
simpletest/ tests/ menu.test - Create a simple hierarchy of links.
- MenuLinksUnitTestCase::testMenuLinkReparenting in modules/
simpletest/ tests/ menu.test - Test automatic reparenting of menu links.
- MenuLinksUnitTestCase::testMenuLinkRouterReparenting in modules/
simpletest/ tests/ menu.test - Test automatic reparenting of menu links derived from menu routers.
File
- includes/
menu.inc, line 3066 - API for the Drupal menu system.
Code
function menu_link_save(&$item, $existing_item = array(), $parent_candidates = array()) {
drupal_alter('menu_link', $item);
// 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'] = (url_is_external($item['link_path']) || $item['link_path'] == '<front>') ? 1 : 0;
// 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,
);
if (isset($item['mlid'])) {
if (!$existing_item) {
$existing_item = db_query('SELECT * FROM {menu_links} WHERE mlid = :mlid', array('mlid' => $item['mlid']))->fetchAssoc();
}
if ($existing_item) {
$existing_item['options'] = unserialize($existing_item['options']);
}
}
else {
$existing_item = FALSE;
}
// Try to find a parent link. If found, assign it and derive its menu.
$parent = _menu_link_find_parent($item, $parent_candidates);
if (!empty($parent['mlid'])) {
$item['plid'] = $parent['mlid'];
$item['menu_name'] = $parent['menu_name'];
}
// If no corresponding parent link was found, move the link to the top-level.
else {
$item['plid'] = 0;
}
$menu_name = $item['menu_name'];
if (!$existing_item) {
$item['mlid'] = db_insert('menu_links')->fields(array(
'menu_name' => $item['menu_name'],
'plid' => $item['plid'],
'link_path' => $item['link_path'],
'hidden' => $item['hidden'],
'external' => $item['external'],
'has_children' => $item['has_children'],
'expanded' => $item['expanded'],
'weight' => $item['weight'],
'module' => $item['module'],
'link_title' => $item['link_title'],
'options' => serialize($item['options']),
'customized' => $item['customized'],
'updated' => $item['updated'],
))->execute();
}
// Directly fill parents for top-level links.
if ($item['plid'] == 0) {
$item['p1'] = $item['mlid'];
for ($i = 2; $i <= MENU_MAX_DEPTH; $i++) {
$item["p$i"] = 0;
}
$item['depth'] = 1;
}
// Otherwise, ensure that this link's depth is not beyond the maximum depth
// and fill parents based on the parent link.
else {
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 router_path.
if (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']);
}
}
// If every value in $existing_item is the same in the $item, there is no
// reason to run the update queries or clear the caches. We use
// array_intersect_key() with the $item as the first parameter because
// $item may have additional keys left over from building a router entry.
// The intersect removes the extra keys, allowing a meaningful comparison.
if (!$existing_item || (array_intersect_key($item, $existing_item) != $existing_item)) {
db_update('menu_links')->fields(array(
'menu_name' => $item['menu_name'],
'plid' => $item['plid'],
'link_path' => $item['link_path'],
'router_path' => $item['router_path'],
'hidden' => $item['hidden'],
'external' => $item['external'],
'has_children' => $item['has_children'],
'expanded' => $item['expanded'],
'weight' => $item['weight'],
'depth' => $item['depth'],
'p1' => $item['p1'],
'p2' => $item['p2'],
'p3' => $item['p3'],
'p4' => $item['p4'],
'p5' => $item['p5'],
'p6' => $item['p6'],
'p7' => $item['p7'],
'p8' => $item['p8'],
'p9' => $item['p9'],
'module' => $item['module'],
'link_title' => $item['link_title'],
'options' => serialize($item['options']),
'customized' => $item['customized'],
))->condition('mlid', $item['mlid'])->execute();
// 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']);
}
// Notify modules we have acted on a menu item.
$hook = 'menu_link_insert';
if ($existing_item) {
$hook = 'menu_link_update';
}
module_invoke_all($hook, $item);
// Now clear the cache.
_menu_clear_page_cache();
}
return $item['mlid'];
}
Comments
Saving internal links
PermalinkIn case you want to save internal links with this function (like links to nodes) in your custom module, and not through the menu admin interface, be sure to validate the item with
menu_edit_item_validate()first like:<?php$form_state = array(
'values' => array(
'menu_name' => $menu_name,
'weight' => $weight,
'link_title' => $link_title,
'link_path' => $link_path,
'module' => 'mymodule',
'mlid' => 0,
),
);
// Validate and transform the item, so it conforms with Drupal standards.
menu_edit_item_validate(array(), $form_state);
// Save the item to database.
menu_link_save($form_state['values']);
?>
If it is not valid it may not be usable or may not load right. Also make sure to rebuild the menu with
menu_rebuild()after you did all the manipulations, as Drupal caches it pretty heavy.None of that
PermalinkYou need to call drupal_get_normal_path() on the link path but not the validate. You need to call menu_cache_clear_all() but not menu_rebuild().
How to add translatable menu items:
PermalinkIn order to add a translatable item the
$itemarray must contain'language'and'customized' => 1. Moreover, differently thannode_save(), themenu_link_save()does not play well with path aliases!!!<?php$menu_item = array(
'link_title' => 'The menu link title',
'menu_name' => 'machine-name-of-the-menu', // It uses '-', not '_'
'customized' => 1, // Must be 1 in order to not dispaly the link in any language
'link_path' => 'node/' . $node->nid, // menu_link_save() does not like aliases. Then 'nid' is used
'language' => 'en', // language is required in order to make the item translatable
);
$mlid = menu_link_save($menu_item); // You don't need to receive it in a variable
$ml = menu_link_load($mlid); // I'm loading $mlid just to display the result and verify it
print_r($ml); // Use print_r() only if you are testing on the block "Execute PHP"
sorry about the code
Permalinksorry about the code formatting, but I could not get it to look nice using the allowed tags.