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