In the last tutorial in this series, Understanding routes and controllers, we looked at the two fundamental steps to programmatically define a custom page in Drupal 8. Today we are going to take this one step further and add a parameter to the route, so that it is dynamic.

Go back to the route defined in the Understanding routes and controllers tutorial. This is what it looks like:

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

With the above code, when you go to /hello, you will get a ’hello world’ message from the controller.

We are about to change the code so we can replace world in hello world with a name that we pass in via the URL. If you hit /hello/bob, the message will become hello bob, hello/alice will become hello alice and so on.

You are going to change this so that you can pass in a person’s name as a parameter.

You can add the name to the line that has the path key:

path: '/hello/{name}'

In the above snippet, {name} is known as the slug and acts as a placeholder. You can name a slug what ever you like, as long as you surround it with accolades ({}). The same name should then be used as an argument in the controller (you’ll see that in a moment). The value will be passed into the controller. In other words, if you hit /hello/alice, then alice is the value for name and alice will be passed to the $name variable in the controller.

The full route will become:

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

Default name

There needs to be a default name, just in case the name is not passed in the URL. This allows you to hit /hello without the name added. To do that, you can add the default name to the defaults part of the route Yaml file.

The default can be whatever you want. I’m going to go with “world”, so that if in the absence of a name, the page content will be “hello world”.

Add the following default name to the route Yaml file:

name: 'world'

So the complete route Yaml file becomes:

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

Add the name to the controller

Now that the route can take the name parameter, you need to use it in the controller for it to take effect.

Take another look at the controller from the previous tutorial:

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

use Drupal\Core\Controller\ControllerBase;

class HelloController extends ControllerBase {
  public function content() {
    return array(
      '#type' => 'markup',
      '#markup' => t('Hello world'),
    );
  }
}

The name is now available to the controller as a variable. The variable is called $name.

public function content()

will become

public function content($name)

To use the variable, you can pass it into the t(). The is the same as it is in Drupal 7.

'#markup' => t('Hello world')

will become

'#markup' => t('Hello @name', array('@name' => $name))

The full controller is now:

<?php
/**
 * @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' => t('Hello @name', array('@name' => $name)),
    );
  }
}

And that is all there is to it. You can now add any name you like to the URL and that will appear as part of the hello message.

/welcome/bob will display hello bob
/welcome/alice will display hello alice
/welcome/jane will display hello jane
/welcome will display hello there (there is the default)

Complete code

Here is the full code for the hello module.

In hello.routing.yml:

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

In src/Controller/HelloController.php:

<?php
/**
 * @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)),
    );
  }
}

And if you haven’t followed along with the previous tutorial, you’ll need a .info file.

In hello.info.yml:

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