Article

Drupal 8 Routing: Decoupling hook_menu

Drupal 8 Routing: Decoupling hook_menu

Drupal 8 is changing the way Drupal developers deliver content to the browser for a particular web page. It replaces parts of hook_menu() with Symphony2's HTTP Kernel. This approach is more object oriented and allows developers to decouple functionality which will make applications more flexible and maintainable.

The Drupal 7 way

Let's take a look at how we would define a route in Drupal 7. Lets say we want to add a new page to our site. In Drupal 7, we need to define an item in hook_menu(), that is an array, with the route as the array key, that defines certain things Drupal needs to know in order to figure out what content to send.

/** * Implements hook_menu(). */ function routing_example_menu() { $items = array(); $items['routing_example/example_page/%/%'] = array( 'title' => 'My Example Page', 'page callback' => 'routing_example_example_page', 'page arguments' => array(2, 3), 'access callback' => 'routing_example_access_callback', 'access arguments' => array('authenticated user'), 'type' => MENU_NORMAL_ITEM, 'menu_name' => 'primary-links', 'weight' => -5, 'file' => 'routing_example.module', ); return $items; }

There are a lot of different things going on here: we are defining the function that will return the content; which arguments are passed to the function; where that function is located; who can see the content; what type of link it is; which menu the link shows in (if any); and, where in the menu it is displayed. That's a lot of information. It tightly couples together the route definition and the definition for the menu link, which are two different things. We could also not have included a menu link by making the type MENU_CALLBACK. So now you can have some items that are just routes, some items that are links, and some items that are both. Hook_menu() is trying to do too many things at once and it can get confusing.

The Drupal 8 way

Drupal 8 solves this problem by breaking out the route definition from the link definition. We now use one yaml file to define routes (module_name.routing.yml), and another to define menu links (module_name.menu_links.yml).

Routing Yaml File

The main parts of the routing yml file:

  • module.route: machine name for the route
  • path (required): is the URL for the route. This would have been your array key in Drupal 7
  • defaults(required): defines how your content is delivered
  • requirements (required): determines conditions that need to be set to allow access
  • options (optional): additional options that will impact the route

routing_example.page: path: 'routing_example/example_page/{name}/{title}' defaults: _content: '\Drupal\routing_example\Controller\ExampleController::examplePage' custom_arg: 'custom' requirements: _role: 'authenticated user'

Here we've defined our path, and the function that will return. Drupal now uses PSR-4 convention (opens in new window) so that your classes are automatically loaded when called. For your controller class to be discovered by Drupal it needs to live inside your module directory in:

[module_directory]/src/Controller/

and define its namespace as:

namepace Drupal\module_name\Controller

The Controller

Our controller code will look like this:

/** * @file * Contains \Drupal\routing_example\Controller\ExampleController. */ namespace Drupal\routing_example\Controller; /** * Example page controller. */ class ExampleController { public function examplePage($custom_arg, $name = NULL, $title = NULL){ $build = array( '#markup' => t("<p>Hello $name $title!</p><p>Custom argument is: $custom_arg</p>"), ); return $build; ); return $build; } }

The examplePage function in our ExampleController will be called when someone visits that URL, and will return our content to the browser. It will also receive three arguments: the custom arg we defined in the defaults, as well as the two arguments we defined in the route. Instead of the old wild card convention (%) in Drupal 7, Drupal 8 uses brackets{} with the argument name. In the requirements section we specify that the user must have the role 'authenticated user' to see this page. Drupal 8 allows you to provide or deny access based on roles, rather than having to define a custom function to restrict pages based on a role. You can find out more information about which options are available for your routing files here. (opens in new window)

Drupal 8 was originally going to replace hook_menu() with another hook, hook_default_menu_links(), but the decision was made to move link definitions to yaml files as well. You can read more about that here: Providing module-defined menu links (opens in new window).

Summary

This is a quick introduction to Drupal 8's new routing system. We showed how Drupal 8 decouples different tasks that were all handled by hook_menu() in Drupal 7. For more information checkout the documentation page at drupal.org: https://drupal.org/developing/api/8/routing (opens in new window).

Get the thinking,
before everyone else.

One considered email a month. New essays, field notes from live engagements, and the occasional strong opinion. No noise.