Menu links with dynamic values

Menu links with dynamic values

Menu link defined statically in a *.links.menu.yml file can be altered with the hook_menu_links_discovered_alter() hook, but if you want to provide a dynamic value you have to use something else.

Let's see first how to alter statically defined menu link.

You can use hook_menu_links_discovered_alter() hook to hide or alter some property of a menu link. For example you can hide the Home menu item in the Main navigation like this:

/**
 * Implements hook_menu_links_discovered_alter().
 */
function MY_MODULE_menu_links_discovered_alter(&$links) {
  unset($links['standard.front_page']);
}

By the way, the Home menu item is defined in the Standard profile here:

https://github.com/drupal/drupal/blob/8.7.4/core/profiles/standard/standard.links.menu.yml

To alter the title you can do something like this:

/**
 * Implements hook_menu_links_discovered_alter().
 */
function MY_MODULE_menu_links_discovered_alter(&$links) {
  $links['standard.front_page']['title'] = t('Altered Title');
}

As you can see hook_menu_links_discovered_alter() hook is very useful, but it's not the right place to implement dynamic behavior. For that you have to use Menu link plugin class.

Let's say that you want to show or hide a menu item depending on the current user role. You can do it this way:

MY_MODULE.links.menu.yml

my_module.user:
  title: 'User'
  description: 'Dynamic menu item.'
  route_name: 'my_module.user'
  parent: 'system.admin'
  class: Drupal\my_module\Plugin\Menu\UserMenuLink

MY_MODULE/src/Plugin/Menu/UserMenuLink.php

<?php

namespace Drupal\MY_MODULE\Plugin\Menu;

use Drupal\Core\Menu\MenuLinkDefault;

class UserMenuLink extends MenuLinkDefault {

  /**
   * {@inheritdoc}
   */
  public function isEnabled() {
    $roles = \Drupal::currentUser()->getRoles();

    if (!in_array('administrator', $roles)) {
      return FALSE;
    }

    return TRUE;
  }

}

Or if you want to provide dynamic menu title you can do it this way:

<?php

namespace Drupal\MY_MODULE\Plugin\Menu;

use Drupal\Core\Menu\MenuLinkDefault;

class UserMenuLink extends MenuLinkDefault {

  /**
   * {@inheritdoc}
   */
  public function getTitle() {
    $roles = \Drupal::currentUser()->getRoles();
    $roles = implode(', ', $roles);
    
    return \Drupal::currentUser()->getDisplayName() . ' - ' . $roles;
  }

}

And to conclude this blog post just a reminder that you should use Dependency Injection to inject the current user if you need it. I used the global Drupal class to make my code shorter.

About the author

Goran Nikolovski is a highly experienced Drupal developer with an extensive skill set that includes PHP, MySQL, HTML, CSS and Javascript. He has experience with large Drupal sites and Drupal Commerce 2.x integration, and he is author of several Drupal modules.