Making a Drupal 8 route dynamic

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:

  1. hello.content:
  2. path: '/hello'
  3. defaults:
  4. _controller: 'Drupal\hello\Controller\HelloController::content'
  5. _title: 'Hello world'
  6. requirements:
  7. _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:

  1. 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:

  1. hello.content:
  2. path: '/hello/{name}'
  3. defaults:
  4. _controller: 'Drupal\hello\Controller\HelloController::content'
  5. _title: 'Hello world'
  6. requirements:
  7. _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:

  1. name: 'world'

So the complete route Yaml file becomes:

  1. hello.content:
  2. path: '/hello/{name}'
  3. defaults:
  4. _controller: 'Drupal\hello\Controller\HelloController::content'
  5. _title: 'Hello world'
  6. name: 'world'
  7. requirements:
  8. _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:

  1. <?php
  2. /**
  3.  * @file
  4.  * Contains \Drupal\hello\Controller\HelloController.
  5.  */
  6. namespace Drupal\hello\Controller;
  7.  
  8. use Drupal\Core\Controller\ControllerBase;
  9.  
  10. class HelloController extends ControllerBase {
  11. public function content() {
  12. return array(
  13. '#type' => 'markup',
  14. '#markup' => t('Hello world'),
  15. );
  16. }
  17. }

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

  1. public function content()

will become

  1. 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.

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

will become

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

The full controller is now:

  1. <?php
  2. /**
  3.  * @file
  4.  * Contains \Drupal\hello\Controller\HelloController.
  5.  */
  6. namespace Drupal\hello\Controller;
  7.  
  8. use Drupal\Core\Controller\ControllerBase;
  9.  
  10. class HelloController extends ControllerBase {
  11. public function content($name) {
  12. return array(
  13. '#type' => 'markup',
  14. '#markup' => t('Hello @name', array('@name' => $name)),
  15. );
  16. }
  17. }

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.

  1. /welcome/bob will display hello bob
  2. /welcome/alice will display hello alice
  3. /welcome/jane will display hello jane
  4. /welcome will display hello there (there is the default)

Complete code

Here is the full code for the hello module.

In hello.routing.yml:

  1. hello.content:
  2. path: '/hello/{name}'
  3. defaults:
  4. _controller: 'Drupal\hello\Controller\HelloController::content'
  5. _title: 'Hello world'
  6. name: 'world'
  7. requirements:
  8. _permission: 'access content'

In src/Controller/HelloController.php:

  1. <?php
  2. /**
  3.  * @file
  4.  * Contains \Drupal\hello\Controller\HelloController.
  5.  */
  6. namespace Drupal\hello\Controller;
  7.  
  8. use Drupal\Core\Controller\ControllerBase;
  9.  
  10. class HelloController extends ControllerBase {
  11. public function content($name) {
  12. return array(
  13. '#type' => 'markup',
  14. '#markup' => $this->t('Hello @name', array('@name' => $name)),
  15. );
  16. }
  17. }

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

In hello.info.yml:

  1. name: Hello
  2. description: An experimental module to build our first Drupal 8 module
  3. package: Custom
  4. type: module
  5. version: 1.0
  6. core: 8.x

Conquer Drupal 8 Module Development

Don't struggle to conquer Drupal 8 module development. Action packed lessons where you will work along building your own real world modules.

20% launch discount available now!

Find out more


Comments

Great post!

There are few typos you probably want to review:
- The first code example - from the previous blog post - already contains the <em>name property, when it shouldn't.
- When defining the <em>name</em> you say "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”." but actually setting it to 'there'.</em>

Blair Wadman's picture

Thanks very much for pointing how the typos Gab! I've updated the post.

You should mention that the order of the parameters in the path doesn't need to match the order of the parameters in the controller method.

so if you have a path like this /hello/{name}/{lastname}

these two signatures are both valid:

content($name, $lastname)
content($lastname, $name)

Also I know the article focuses on the aspect of the path, but the use of the t() function inside of a method is considered bad practice. You should use $this->t(), for better testability. t() function is for non-OOP code.

Blair Wadman's picture

Great feedback, thanks Mihhail. Good catch re t(). I'm going to write a post on t() and $this->t() but will update this one in the mean time.

Add new comment