I have seen How do I create menu links programmatically? but how do I update or delete menu items in Drupal 8?
By programmatically, I mean dynamically; YAML configuration files are not going to help.
I have seen How do I create menu links programmatically? but how do I update or delete menu items in Drupal 8?
By programmatically, I mean dynamically; YAML configuration files are not going to help.
First you will have to load the entity. Either way works:
$menu_link = MenuLinkContent::load($menu_link_id);
or ...
$menu_link = \Drupal::entityTypeManager()->getStorage('menu_link_content')->load($menu_link_id);
Next you can update value using set()
method or through the magic method __set...
$menu_link->expanded = TRUE;
To save, simply call the save()
method.
To delete, simply call the delete()
method.
I had to do some thing like ....
$result = $this->menuLinkManager->loadLinksByRoute('entity.node.canonical', ['node' => $node->id()]);
foreach ($result as $menu_item) {
if (is_object($menu_item)) {
$id = $menu_item->getPluginDefinition()['metadata']['entity_id'];
$menu_link = $this->entityTypeManager->getStorage('menu_link_content')->load($id);
$menu_link->enabled = 0;
$menu_link->save()
}
}
Probably too late. I did like:
$menu_handler = \Drupal::service('menu.link_tree')
->load("menu-name", new \Drupal\Core\Menu\MenuTreeParameters());
foreach ($menu_handler as $key => $value) {
if ('Name' == $value->link->getTitle()) {
$link = $value->link;
$link->updateLink(
[
'enabled' => '1',
'title' => 'New Name',
],
TRUE
);
}
}
updateLink()
was unavailable for me, but $value->link = ['uri' => 'internal:/my-path']; $value->save();
worked.
This is how i delete all the links in a particular menu like footer
$old_menu_links = \Drupal::entityTypeManager()->getStorage('menu_link_content')
->loadByProperties(['menu_name' => 'footer']);
foreach ($old_menu_links as $old_menu_link) {
$old_menu_link->delete();
}
And to update existing menu,
$menu_links = \Drupal::entityTypeManager()->getStorage('menu_link_content')
->loadByProperties(['menu_name' => 'footer']);
foreach ($menu_links as $menu_link) {
if ($menu_link->gettitle() == 'old-title') {
$menu_link->set('title', 'new-title');
$menu_link->save();
}
}
This turned out to be insanely complicated. Apparently not every menu link is a MenuLinkContent
object. Sometimes it is a MenuLinkDefault
object. In that case, you cannot use ->set()
or ->save()
. This is the most complete answer to cover both cases. Put this inside of an hook_update_n()
and change $disabled_routes
and $menu
to your needs. In this example I am disabling menu links.
$disable_routes = [
'<front>',
'view.frontpage.page_1',
'private_message.private_message_page',
'view.opigno_calendar.page_month',
];
$menu_name = 'main';
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
$menu_link_tree_elements = \Drupal::service('menu.link_tree')
->load($menu_name, new \Drupal\Core\Menu\MenuTreeParameters());
foreach ($disable_routes as $disable_route) {
foreach ($menu_link_tree_elements as $key => $menu_link_tree_element) {
$menu_link_default = $menu_link_tree_element->link;
$plugin_definition = $menu_link_default->getPluginDefinition();
$link_route = $plugin_definition['route_name'];
if ($link_route == $disable_route) {
$uuid = $menu_link_default->getDerivativeId();
if (!empty($uuid)) {
$menu_link_content = \Drupal::service('entity.repository')
->loadEntityByUuid('menu_link_content', $uuid);
$menu_link_content->set('enabled', 0);
$menu_link_content->save();
}
else {
$menu_link = $menu_link_manager->getDefinition($key);
$menu_link['enabled'] = 0;
$menu_link_manager->updateDefinition($key, $menu_link);
// Not sure if this is necessary.
$menu_link_default->updateLink(
[
'enabled' => 0,
],
true
);
}
}
}
}
My use case was, that I had a leftover menu link, when uninstalling the CAS module, due to this core issue https://www.drupal.org/project/drupal/issues/3324291. Since I could not load the menu link via \Drupal::entityTypeManager()->getStorage('menu_link_content')->load($menu_link_id);
, because this entry was only in the menu_tree
db table and not in menu_link_content(_data)
db tables (looks like that this is because of the core issue and not standard behavior, but I didn't investigate further here), this is what I did (it was a MenuLinkDefault
object and not a MenuLinkContent
object for some context also):
/** @var \Drupal\Core\Menu\MenuTreeStorageInterface $menu_tree_storage */
$menu_tree_storage = \Drupal::service('menu.tree_storage');
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
$cas_menu_link = $menu_link_manager->loadLinksByRoute('cas.settings');
if (!empty($cas_menu_link)) {
// Array keys are plugin IDs.
$menu_tree_storage->delete(array_key_first($cas_menu_link));
}
The delete function here removes the data only from the menu_tree table, keep that in mind. But it also updates all the childred/parents of the deleted link in the tree structure. If you delete MenuLinkContent
entities (menu_link_content _data db tables), this process is also done via the preDelete function in the entity class, so the tree storage is also updated.
So to sum it up, this solution is for purging of orphaned data in the menu_tree table, on incorrect deletion of the MenuLinkContent entities, due to a core issue.