Link Templates

Last updated on
7 August 2022

The links annotation in an entity definition specifies an array of link templates. The keys are link relation types, and the values are URL paths with placeholders to be replaced.

For example, the annotations for Drupal\media\Entity\Media include

 *   links = {
 *     "add-page" = "/media/add",
 *     "add-form" = "/media/add/{media_type}",
 *     "canonical" = "/media/{media}/edit",
 *     "collection" = "/admin/content/media",
 *     "delete-form" = "/media/{media}/delete",
 *     "delete-multiple-form" = "/media/delete",
 *     "edit-form" = "/media/{media}/edit",
 *     "revision" = "/media/{media}/revisions/{media_revision}/view",
 *   }

Instead of defining link templates statically in the entity annotations, they can also be defined dynamically using setLinkTemplate(). See the section "Working with link templates" below.

In the link templates, the placeholders {media_type}, {media}, and {media_revision} are replaced with the media type (bundle), media ID, and revision ID. See the API doc EntityType::getLinkTemplates() for more information.

The link relation types canonical, delete-form, and many more are declared in core.link_relation_types.yml. A module can define additional link relation types. For example, workflows.link_relation_types.yml defines add-state-form and add-transition-form:

add-state-form:
  uri: https://drupal.org/link-relations/add-state-form
  description: A form where a state can be created.
add-transition-form:
  uri: https://drupal.org/link-relations/add-transition-form
  description: A form where a transition can be created.

Using drush, you can generate a list of all the available link relation types:

drush php:eval 'print_r(array_keys(Drupal::service("plugin.manager.link_relation_type")->getDefinitions()))'

Defining routes

Routes for these URL paths can be defined statically (in a .routing.yml file) or dynamically in a route provider. In either case, the route name should be "entity.$entity_type_id.$link_type", where $link_type is the link relation type with '-' replaced by '_'.

For example, media.routing.yml defines the route for one of the links shown in the first section:

entity.media.revision:
  path: '/media/{media}/revisions/{media_revision}/view'
  defaults:
    _controller: '\Drupal\Core\Entity\Controller\EntityViewController::viewRevision'
    _title_callback: '\Drupal\Core\Entity\Controller\EntityController::title'
  options:
    ...

The entity annotations (handlers.route_provider) can specify one or more route provider. Even though "route_provider" is singular, more than one is allowed. Looking at Drupal\media\Entity\Media again, the annotations include

 *   handlers = {
 *     ...
 *     "route_provider" = {
 *       "html" = "Drupal\media\Routing\MediaRouteProvider",
 *     },
 *     ...
 *   },

The routes for three more of the links in the first section are defined in Drupal\media\Routing\MediaRouteProvider::getRoutes(). That class extends Drupal\Core\Entity\Routing\AdminHtmlRouteProvider.

For more information, see the Entity Routes section in the API pages for the Entity API.

Drupal\Core\Entity\EntityType has methods

  • getLinkTemplate()
  • getLinkTemplates()
  • hasLinkTemplate()
  • setLinkTemplate()

Example: config_translation_entity_type_alter() adds the config-translation-overview link to config entity types:

      /** @var \Drupal\Core\Entity\EntityTypeInterface $entity_type */
      if ($entity_type->hasLinkTemplate('edit-form')) {
        $entity_type->setLinkTemplate('config-translation-overview', $entity_type->getLinkTemplate('edit-form') . '/translate');
      }

Drupal\Core\Entity\EntityBase has methods

  • linkTemplates()
  • hasLinkTemplate()
  • toUrl()
  • toLink()

The first method is used in EntityBase::toUrl(). Notice how the route name is derived from the link relation type:

    // The links array might contain URI templates set in annotations.
    $link_templates = $this->linkTemplates();

    // ...

    if (isset($link_templates[$rel])) {
      $route_parameters = $this->urlRouteParameters($rel);
      $route_name = "entity.{$this->entityTypeId}." . str_replace(['-', 'drupal:'], ['_', ''], $rel);
      $uri = new Url($route_name, $route_parameters);
    }

 The second method is used in Drupal\user\RoleListBuilder::getDefaultOperations() when adding the "Edit permissions" link to each row of /admin/people/roles:

    if ($entity->hasLinkTemplate('edit-permissions-form')) {
      $operations['permissions'] = [
        'title' => t('Edit permissions'),
        'weight' => 20,
        'url' => $entity->toUrl('edit-permissions-form'),
      ];
    }

The last two methods accept a link relation type as an optional parameter (defaults to 'canonical'). The snippet above has an example of toUrl(). A common use of toLink() is to generate the edit link for a log message. For example, Drupal\taxonomy\TermForm::save() has

    $edit_link = $term->toLink($this->t('Edit'), 'edit-form')->toString();

The Node module adds a permissions form for the Article content type at /admin/structure/types/manage/article/permissions with these annotations in core/modules/node/src/Entity/NodeType.php:

 *   handlers = {
 *     ...
 *     "route_provider" = {
 *       "permissions" = "Drupal\user\Entity\EntityPermissionsRouteProvider",
 *     },
 *     "list_builder" = "Drupal\node\NodeTypeListBuilder",
 *   },
 *   ...
 *   links = {
 *     "edit-form" = "/admin/structure/types/manage/{node_type}",
 *     "delete-form" = "/admin/structure/types/manage/{node_type}/delete",
 *     "entity-permissions-form" = "/admin/structure/types/manage/{node_type}/permissions",
 *     "collection" = "/admin/structure/types",
 *   },

The real work is done in the User module, starting with core/modules/user/user.link_relation_types.yml:

# User extension relation types.
# See https://tools.ietf.org/html/rfc5988#section-4.2.
entity-permissions-form:
  uri: https://drupal.org/link-relations/permissions
  description: A form where bundle-related permissions can be managed.

The route is defined in EntityPermissionsRouteProvider, which sets the form builder Drupal\user\Form\EntityPermissionsForm as the page controller.

Help improve this page

Page status: No known problems

You can: