Below is an extract from my book Drupal 9 Module Development from the early Chapter 5 (out of 18) on Menu Links. I introduce the menu system from a rather theoretical point of view, and catalogue the different types of menu links we have in Drupal.
Before we get our hands dirty with menus and menu links, let's talk a bit about the general architecture behind the menu system. To this end, I want to see its main components, what some of its key players are and what classes you should be looking at. As always, no great developer has ever relied solely on a book or documentation to figure out complex systems.
Menus
Menus are configuration entities represented by the following class: Drupal\system\ Entity\Menu
. I previously mentioned that we have something called configuration entities in Drupal, which we explore in detail later in this book. However, for now, it's enough to understand that menus can be created through the UI and become an exportable configuration. Additionally, this exported configuration can also be included inside a module so that it gets imported when the module is first installed. This way, a module can ship with its own menus. We will see how this latter aspect works when we talk about the different kinds of storage in Drupal. For now,
we will work with the menus that come with Drupal core.
Each menu can have multiple menu links, structured hierarchically in a tree with a maximum depth of 9 links. The ordering of the menu links can be done easily through the UI or via the weighting of the menu links, if defined in code.
Menu links
At their most basic level, menu links are YAML-based plugins. To this end, regular menu links are defined inside a module_ name.links.menu.yml
file and can be altered by other modules by implementing hook_menu_links_discovered_alter()
. When I say regular, I mean those links that go into menus. We will see shortly that there are also a few other types.
There are a number of important classes you should check out in this architecture though: MenuLinkManager
(the plugin manager) and MenuLinkBase
(the menu link plugins base class that implements MenuLinkInterface
).
Menu links can, however, also be content entities. The links created via the UI are stored as entities because they are considered content. The way this works is that for each created MenuLinkContent
entity, a plugin derivative is created. We are getting dangerously close to advanced topics that are too early to cover. But in a nutshell, via these derivatives, it's as if a new menu link plugin is created for each MenuLinkContent
entity, making the latter behave as any other menu link plugin. This is a very powerful system in Drupal.
Menu links have a number of properties, among which is a path or route. When created via the UI, the path can be external or internal or can reference an existing resource (such as a user or piece of content). When created programmatically, you'll typically use a route.
Multiple types of menu links
The menu links we've been talking about so far are the links that show up in menus. There are also a few different kinds of links that show up elsewhere but are still considered menu links and work similarly.
Local tasks
Local tasks, otherwise known as tabs, are grouped links that usually show up above the main content of a page (depending on the region where the tabs block is placed). They are usually used to group together related links that have to deal with the current page. For example, on an entity page, such as the node detail page, you can have two tabs—one for viewing the node and one for editing it (and maybe one for deleting it); in other words, local tasks:
Local tasks take access rules into account, so if the current user does not have access to the route of a given tab, the link is not rendered. Moreover, if that means only one link in the set remains accessible, that link doesn't get rendered as there is no point. So, for tabs, a minimum of two links are needed for them to show up.
Modules can define local task links inside a module_name.links.task.yml
file, whereas other modules can alter them by implementing hook_menu_local_tasks_ alter()
.
Local actions
Local actions are links that relate to a given route and are typically used for operations. For example, on a list page, you might have a local action link to create a new list item, which will take you to the relevant form page.
Modules can define local action links inside a module_name.links.action.yml
file, whereas other modules can alter them by implementing hook_menu_local_ actions_alter()
.
Contextual links
Contextual links are used by the Contextual module to provide handy links next to
a given component (a render array). You probably encountered this when hovering over a block, for example, and getting that little icon with a dropdown that has the Configure block link.
Contextual links are tied to render arrays. In fact, any render array can show a group of contextual links that have previously been defined.
Modules can define contextual links inside a module_name.links.contextual.yml
file, whereas other modules can alter them by implementing hook_contextual_links_ alter()
.
For more on the menu system and to see how the twist unfolds, do check out my book, Drupal 9 Module Development.
Thanks for the support.