I need to add some links to existing menu (or to new) programmatically in Drupal 8.
4 Answers
In order to create a menu item automatically this can be placed in a hook_update_N()
implementation in the .install file; it will run when the database is updated.
/**
* Put the following line on the top of the file containing the
* following code.
/*
use Drupal\menu_link_content\Entity\MenuLinkContent;
$items = [
'1' => 'Menuitem 1',
'2' => 'Menuitem 2',
'3' => 'Menuitem 3'
];
foreach($items as $nid => $title) {
$menu_link = MenuLinkContent::create([
'title' => $title,
'link' => ['uri' => 'internal:/node/' . $nid],
'menu_name' => 'main',
'expanded' => TRUE,
]);
$menu_link->save();
}
You can also programmatically create an entire menu.
$menu = \Drupal::entityTypeManager()
->getStorage('menu')
->create([
'id' => 'menu_test',
'label' => 'Test menu',
'description' => 'Description text',
]);
$menu->save();
-
1
-
6
-
Note. For external links, use the syntax 'link' => ['uri' => 'example.com'] Apr 26, 2018 at 22:30
-
As of 8.6 and 8.7, I ran into problems using "internal:/node/123". It seems to read 'internal' and flag the link for re-discovery. Instead, using "entity:node/123" seems to work as expected. That is what is stored in the DB when you create a link via menu UI as well.– BWagnerJun 21, 2019 at 16:27
If you would like to create module-defined menu links, add something like this example to your custom_module.links.menu.yml file:
custom_module.admin_item_1:
title: 'New Admin Item 1'
parent: system.admin
description: 'Description of link goes here.'
route_name: view.some_view_id.page_1
parent
(optional) is the id
column of the parent in the menu_tree
table, and route_name
is Drupal's internal route ID for where you'd like the menu item to link to. It's in the menu_tree
table as route_name
.
See Providing module-defined menu links and Add a menu link for more details and options.
-
3Menu yamls only help if you know in advance what menu items you want to create, Programmatic creation may be on node save. May 4, 2017 at 8:04
-
1How to add translatable menu item that way? Like one menu item, but in 3 languages (different menu item titles and paths).– MilanGSep 10, 2018 at 9:39
To expand on @skorzh answer and reply to the comment from @Matt, if you want to nest a menu item inside another you have to get the parent id and set it as 'parent'. As an example this code nests the item inside the top level using the variable $top_level
below.
$menu_link_storage = \Drupal::entityTypeManager()->getStorage('menu_link_content');
$my_menu = $menu_link_storage->loadByProperties(['menu_name' => 'my-menu-name']);
foreach ($my_menu as $menu_item) {
$parent_id = $menu_item->getParentId();
if (!empty($parent_id)) {
$top_level = $parent_id;
break;
}
}
$menu_link_storage->create([
'title' => 'My menu link title',
'link' => ['uri' => 'internal:/my/path'],
'menu_name' => 'my-menu-name',
'parent' => $top_level,
'expanded' => TRUE,
'weight' => 0,
])->save();
-
2The parent ID is a concatenated string containing
menu_link_content:
and a menu item's UUID. For example$parent_id = 'menu_link_content:' . $menu_item->uuid();
.– leymannxAug 17, 2021 at 21:12
For anyone who want to create a menu link associated with a node, you should put the format below for the link URI
entity:node/{node-id}
instead of
internal:/node/{node-id}
For example:
MenuLinkContent::create([
'title' => 'Menu Title',
'link' => [
'uri' => "entity:node/{$node_id}",
],
'menu_name' => $menu_name,
'parent' => "$menu_name:",
])->save();