Converting route parameters into objects

When working with routes for an entity, you have probably created a route path with parameter in the following format /node/{node}/edit where name node was your custom entity ID.

What is interesting about this path format is that the parameter {node} gets converted into an entity object and is later available inside your controller action as an argument. The process is called route upcasting. Let's dig deeper to see how it works.

Parameter converters

Parameter converters are special services tagged as paramconverter, which process our route parameters. To better understand this, we can look at the node module's route entity.node.preview.

This route has node_preview parameter defined which comes in the form of a UUID (from the URL: /node/preview/986f7150-826d-44b3-9b73-f560b8f0c32a/full).

entity.node.preview:
  path: '/node/preview/{node_preview}/{view_mode_id}'
  defaults:
    _controller: '\Drupal\node\Controller\NodePreviewController::view'
    _title_callback: '\Drupal\node\Controller\NodePreviewController::title'
  requirements:
    _node_preview_access: '{node_preview}'
  options:
    parameters:
      node_preview:
        type: 'node_preview'

Route parameter node_preview in this case is a special service with the same service ID name and a class name called Drupal\node\ParamConverter\NodePreviewConverter. Param converters need to implement a method called convert which is responsible for converting those route parameters. In our case, it gets converted into the node entity by the method NodePreviewConverter::convert.

After the conversion entity will get loaded and it will be available in NodePreviewController::view action.

File: core/modules/src/Controller/NodePreviewController.php

/**
* {@inheritdoc}
*/
public function view(EntityInterface $node_preview, $view_mode_id = 'full', $langcode = NULL) {
    $node_preview->preview_view_mode = $view_mode_id;
    $build = parent::view($node_preview, $view_mode_id);

    $build['#attached']['library'][] = 'node/drupal.node.preview';

    // Don't render cache previews.
    unset($build['#cache']);

    return $build;
}

Creating a custom parameter converter

Parameter converter implements the \Drupal\Core\ParamConverter\ParamConverterInterface with the following two methods:

  • applies: Determine to which route this converter applies.
  • convert: Convert the path variable to the corresponding object.

Let's look at the example where we want to load the entity by UUID as the path parameter. First, let's create our param converter implementation.

<?php

namespace Drupal\module_name\ParamConverter;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\ParamConverter\ParamConverterInterface;
use Symfony\Component\Routing\Route;

/**
 * Provide upcasting for entity preview.
 */
class EntityPreviewConverter implements ParamConverterInterface {

  /**
   * Entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * {@inheritdoc}
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager) {
    $this->entityTypeManager = $entityTypeManager;
  }

  /**
   * {@inheritdoc}
   */
  public function convert($value, $definition, $name, array $defaults) {
    return $this->entityTypeManager->getStorage('ENTITY_TYPE')->loadByProperties(['uuid' => $value]);
  }

  /**
   * {@inheritdoc}
   */
  public function applies($definition, $name, Route $route) {
    if (!empty($definition['type']) && $definition['type'] == 'entity_preview') {
      return TRUE;
    }
    return FALSE;
  }

}

Next, we register our tagged service paramconverter.

services:
  entity_preview:
    class: Drupal\module_name\ParamConverter\EntityPreviewConverter
    arguments:
      - '@entity_type.manager'
    tags:
      - { name: paramconverter }

The final step is to create our route where we can use this new parameter type.

entity.ENTITY_TYPE.preview:
  path: '/entity_type/{entity_preview}/preview'
  defaults:
    _controller: '\Drupal\module_name\Controller\EntityController::preview'
  requirements:
    _permission: 'administer content'
  options:
    parameters:
      entity_preview:
        type: 'entity_preview'

Resources:

Add new comment

CAPTCHA
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.