menu_link_save
- Versions
- 6 – 7
menu_link_save(&$item)
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
Code
includes/menu.inc, line 2544
<?php
function menu_link_save(&$item) {
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,
);
$existing_item = FALSE;
if (isset($item['mlid'])) {
if ($existing_item = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item['mlid']))->fetchAssoc()) {
$existing_item['options'] = unserialize($existing_item['options']);
}
}
if (isset($item['plid'])) {
if ($item['plid']) {
$parent = db_query("SELECT * FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $item['plid']))->fetchAssoc();
}
else {
// Don't bother with the query - mlid can never equal zero..
$parent = FALSE;
}
}
else {
$query = db_select('menu_links');
// 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') {
$query->condition('module', 'system');
}
else {
// If not derived from a router item, we respect the specified menu name.
$query->condition('menu_name', $item['menu_name']);
}
// Find the parent - it must be unique.
$parent_path = $item['link_path'];
do {
$parent = FALSE;
$parent_path = substr($parent_path, 0, strrpos($parent_path, '/'));
$new_query = clone $query;
$new_query->condition('link_path', $parent_path);
// Only valid if we get a unique result.
if ($new_query->countQuery()->execute()->fetchField() == 1) {
$parent = $new_query->fields('menu_links')->execute()->fetchAssoc();
}
} 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) {
$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();
}
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 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_assoc() 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_assoc($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'];
}
?>Login or register to post comments 