Introduction to creating menu links in a custom Drupal 8 module

A few weeks ago I wrote about routes and controllers in Drupal 8 and making them a route dynamic. This week we are going to look at adding a link to Drupal 8’s menu system in a custom module.

Before we get into the menu system, let’s take a few moments to recap on routes.

What is a route?

A route determines which code should be run to generate the response when a URI is requested. It does this by mapping a URI to a controller class and method. This defines how Drupal deals with a specific URI. Routes are based on Symfony’s routing system. Routes are defined in their own YAML file in the root of the module folder and take the format modulename.routing.yml

What a route doesn’t do is add a link to the menu system for that URI. In Drupal 8, that is decoupled from the route registration.

How is this different to Drupal 7?

In Drupal 7, you add a route and menu item in the same place, in hook menu.

This is how it looks in Drupal 7:


/** * Implements hook_menu(). */ function hello_menu() { $items['hello/custom'] = array( 'title' => 'Custom page', 'page callback' => ‘hello_custom', 'access arguments' => array('access content'), ); return $items; }

The above code snippet will register the URL hello/custom, map it to a callback function that will generate the content and add it to the menu system.

Create a menu link

To demonstrate the Drupal 8 menu system, we will add a menu link to the main navigation menu.

Before we do that, we need to know the name of the menu to add the menu link to. If you head over to Structure -> Menus in a Drupal 8 site, you will see existing menu’s.

Main navigation is the one that we want. Click on Edit menu and you’ll see the links already defined for the menu. If this is a fresh install, it will probably just be Home.

Have a look at the path for this page:

admin/structure/menu/manage/main

The last part is the name of the menu, main. We will need this when adding a menu link to this.

With that out of the way, let’s add the menu link.

  • In your module root, create hello.links.menu.yml
  • Add the following:
hello.demo:
  title: 'Hello'
  description: 'Hello page'
  parent: main
  menu_name: main
  route_name: hello.content`

Let’s break this down.

  • hello.demo - the namespace for this menu link.
  • title - this will appear as the menu link
  • description - this will appear as the menu link title, a tooltip and/or the description in the admin UI.
  • parent - the parent item, which is used if you are adding this item as a child of another in a hierarchy. In this case the parent is the menu itself, so we are using the menu_name.
  • menu_name - this is the menu name we found in the previous step._
  • route_name - the route to call for this menu link.

You will need to clear the cache in order for the menu item to appear.

If you want to nest the menu link under another menu link, then use the parent key and assign it the namespace of the parent menu link. As an example, let’s nest this menu link under the existing Home menu link.

hello.demo:
  title: 'Hello'
  description: 'Hello page'
  parent: standard.front_page
  menu_name: main
  route_name: hello.content

You are probably wondering where I got the standard.front_page from for the Home menu link. There are two ways to find names for parent links.

  1. Look through module’s *.links.menu.yml files
  2. Inspect the URL of the menu link

Finding the appropriate .links.menu.yml can be tricky. The easiest way is to do a search through the code base. If I do a find for title: 'Home' then I’ll find the menu links YAML file at core/profiles/standard/standard.links.menu.yml.

Inspecting the URL of the menu link is far easier (let me know in the comments below if you have a better method). If you click on the edit button along side the Home menu link, and check out the path:

admin/structure/menu/link/standard.front_page/edit

The menu link namespace is part of the path. This is made up of the module name (standard) and the menu link name (front_page), separated with a dot.

Go back and look at the main menu admin screen. You will see that the Hello link is now nested under Home.

Admin menu

If you have an admin form, you might want your form to be listed in the Configuration section of the admin interface. To do that, the parent will be one of the menu links listed under Administration. Just like above, you can find the name spaced menu name via the menu interface.

Under Structure > Menu’s, click on Edit Menu for the Administration link.

Scroll down until you see the Development link, which is nested under Configuration. Click on edit. The path should be:

admin/structure/menu/link/system.admin_config_development/edit

So in this case, the name of the parent will be system.admin_config_development.

hello.admin:
  title: 'Hello module settings'
  description: 'Module settings form'
  parent: system.admin_config_development
  route_name: hello.content
  weight: 100

To make it easier, here is a list of the main menu links under admin configuration that you can use.

  • People: user.admin_index
  • System: system.admin_config_system
  • Content authoring: system.admin_config_content
  • User Interface: system.admin_config_ui
  • Media: system.admin_config_media
  • Search and metadata: system.admin_config_search
  • Regional and language: system.admin_config_regional
  • Web services: system.admin_config_services
  • Workflow: system.admin_config_workflow

Complete code

Here is the full code for this example. If you have already been following along with previous tutorials in this series and created the hello module, you could skips to step 5 and 6.

Step 1: Create a new directory in the modules directory called hello.

Step 2: In the hello directory, create a file called hello.info.yml and add the following code to that file:

name: Hello
description: An experimental module to build our first Drupal 8 module
package: Custom
type: module
version: 1.0
core: 8.x

Step 3: In the hello directory, create a file for the route called hello.routing.yml and add the following code to that file:

hello.content:
  path: '/hello/{name}'
  defaults:
    _controller: 'Drupal\hello\Controller\HelloController::content'
    _title: 'Hello world'
    name: 'there'
  requirements:
    _permission: 'access content'

Step 4: In the hello directory, create a new directory called src and inside that create a directory called Controller. In the Controller directory, create a file for the controller called HelloController.php and add the following code to it. Add open PHP tags to the top of the file (I currently can't add this to the code snippet as it breaks the code highlighter).

/**
 * @file
 * Contains \Drupal\hello\Controller\HelloController.
 */
namespace Drupal\hello\Controller;

use Drupal\Core\Controller\ControllerBase;

class HelloController extends ControllerBase {
  public function content($name) {
    return array(
      '#type' => 'markup',
      '#markup' => $this->t('Hello @name', array('@name' => $name)),
    );
  }
}

Step 5: In the hello directory, create a file for the menu links called hello.links.menu.yml and add the following code to it:

 hello.demo:
   title: 'Hello'
   description: 'Hello page'
   parent: main
   menu_name: main
   route_name: hello.content

Step 6: Optionally change the menu link and nest it under the Home menu link:

 hello.demo:
   title: 'Hello'
   description: 'Hello page'
   parent: standard.front_page
   menu_name: main
   route_name: hello.content

Step 7: Clear the cache


In most cases, you would create a menu link in a custom module for an admin form. In a few week’s, we’ll be looking at creating admin forms in Drupal 8 and creating a menu link for it. Jump on the newsletter if you want to be the first to get it.

Comments

Hi,
I have followed the steps and its not working.. not sure why..
could you zip all the files and share it ?

Thanks

Hi Blair,

The hello module worked perfectly for me. Your directions were clear and concise.

Thanks,
Phil

Add new comment