Parameter upcasting in routes

Last updated on
11 October 2023

Routes in Drupal 8 or later may include placeholder elements which designate places where the URL contains dynamic values. By naming these placeholders, the system can upcast (convert) those values to actual object instances. For example if a node's base path is '/node/{node}', then {node} is a placeholder.

The ParamConverter system takes care of converting that parameter to a node object instance automatically (because there exists a content entity called node).

Menu parameter upcasting means converting a menu argument to anything which could be used in the route controllers. It can be an object or an array.

Let's take an example of the following code in Drupal 7:
In my_module.module

function my_module_menu() {
  $items['node/%my_menu/mytab'] = array(
    // ...
    // ...
  );
}

The my_module_menu() function implementing hook_menu() shows a menu item with an argument %my_menu. Suppose we want the callback function for this menu item to receive an object after doing some processing on the value passed from the url. e.g., we want to load a specific field of the node with nid 1 when we visit node/1/mytab.

To accomplish the above in Drupal 7, would require us to create a loader function like the one below:

function my_menu_load($arg) {
  // Do whatever with argument and return your values
}

The page callback for the menu would receive whatever is returned from the loader function defined above.

To accomplish same in Drupal 8 or higher makes use of ParamConverter interface. To port the example mentioned above, we will need to do the following:

  1. Create my_module.routing.yml
  2. Create my_module.services.yml describing metadata for your custom paramconverter implementing the paramconverter interface
  3. Implement the custom paramconverter in a PHP class namespaced in my_module.services.yml
  4. Implement the callback for your menu item defined in my_module.routing.yml

Porting your menu item to Drupal 8 or later

In my_module.routing.yml

my_module.mymenu:
  path: '/node/{my_menu}/mytab'
  defaults:
    _title: 'My Title'
    _form: '\Drupal\mymodule\Form\MyModuleformControllerForm'
  options:
    parameters:
      my_menu:
        type: my_menu

This is what a typical route would look like in Drupal 8. The route described above is going to render a form on the page depending on the my_menu argument passed down to it.

It's very important that the name of the parameter matches the variable in the page callback arguments. e.g., if the parameter name is declared as my_menu in routing.yml file, the callback function would receive the upcasted value in the $my_menu variable.

Special case: Entity ID parameters

In the case that a route parameter matches the ID of an entity type, you do not need to implement the ParamConverter class. In routing.yml, simply write type: entity:my_entity_type instead of type: my_menu.

In the case that you are loading your own custom entity Controller implementation, it is important that

  • The actual variable name of the entity in the Controller method callback argument should be the identical to the route parameter name. If the route parameter name is {foobar} then the variable name must be $foobar. The route parameter name is typically defined either in a module's routing.yml file, or in the entity classes annotation in the links section.
  • The variable for the entity in the controller must be type hinted on the entity class, e.g. \Drupal\foobar\Entity\Foobar $foobar.
foobar.view:
  path: '/foobar/{foobar}'
  defaults:
    _controller: '\Drupal\foobar\Controller\Foobar::content'
    _title: 'Oh yeah foobar'
  requirements:
    _access: 'TRUE'

And the Controller Implementation

class Foobar extends ControllerBase {
  public function content(\Drupal\foobar\Entity\Foobar $foobar, Request $request) {
    return [
      '#markup' => 'cheesus slice',
    ];
  }
}

Upcasting route slug name options

Routing parameters are not limited to raw entity IDs (like /node/123) but can also use arbitrary string placeholders known as "slugs". A slug is a short human readable name that uniquely identifies a piece of content. For example if you have a node called "The best recipe for Crème Brûlée!" then you can identify this with the slug best-recipe-creme-brulee. You can use the options: for defining slugs:

foobar.view:
  path: '/foobar/{foobar_placeholder}'
  defaults:
    _controller: '\Drupal\foobar\Controller\Foobar::content'
    _title: 'Oh yeah foobar'
  options:
    parameters:
      foobar_placeholder:
        type: entity:foobar

In this example, my entity type is called foobar and the first argument on the callback should be named the same as the slug name.

class Foobar extends ControllerBase {
  public function content(\Drupal\foobar\Entity\Foobar $foobar_placeholder, Request $request) {
  }
}

Building a Parameter Converter service

See https://www.drupal.org/docs/8/api/routing-system/implementing-custom-parameter-converters

Content is originally taken from : http://www.qed42.com/blog/upcasting-menu-parameters-drupal-8

Help improve this page

Page status: No known problems

You can: