Get media entity path or url in twig template for drupal 9, 10; canonical route to individual media content item

This sort of thing makes Drupal just bad. Like i know (and Drupal knows) this is information i can get, but rather than follow the paths of convenience that have been developed for the things people have been using regularly for 20 years (nodes) we add a new front-end-facing entity and in the name of “consistency” we make it damn near impossible to figure

While , i thought content entities in general did have the concept of a canonical path.

Or anything broadly equivalent and easy to guess…

{{ media.getUrl() }} or {{ media.getPath() }} or url: {{media.url}} or path: {{media.path.0.value }} or anything, at least id: {{}} provided a value.

Looking it up, <a href="{{ path('media.entity.canonical', {'media':}) }}"> should work the same as it can for node, entity.node.canonical, but got this:

The website encountered an unexpected error. Please try again later.

Symfony\Component\Routing\Exception\RouteNotFoundException: Route “media.entity.canonical” does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 206 of core/lib/Drupal/Core/Routing/RouteProvider.php).

So we are not giving simple URL or path functions or variables in the name of consistency, but then our routes for entities, which is what we have to use instead to get the path, also lack any consistency. Got it.

EDIT: Route names are consistent, all the above investigation was unnecessary and only happened because i am dyslexic.

So the solution is as expected:

  <a href="{{ path('', {'media':}) }}">

OK, fine, let’s look in core/modules/media/media.routing.yml. We see and nothing else even close. OK looking in core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php we see there absolutely should be a route available at, if there is a canonical route for media, so… there must not be?

    if ($canonical_route = $this->getCanonicalRoute($entity_type)) {
      $collection->add("entity.{$entity_type_id}.canonical", $canonical_route);

Like i know we had to turn it on in the interface but it seems there should be one under the hood always?

OK in core/modules/media/src/Routing/MediaRouteProvider.php we have this:

   * {@inheritdoc}
  protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
    if ($this->config->get('standalone_url')) {
      return parent::getCanonicalRoute($entity_type);
    else {
      return parent::getEditFormRoute($entity_type);

OK… so ignoring for the moment the fact that the “canonical” route changes based on that setting, what the hell is the canonical route called if not

template_preprocess_node has this:

  $variables['url'] = !$node
    ->isNew() ? $node
    ->toString() : NULL;

The formatting there makes it unnecessarily hard to parse, so to rephrase, if the node is new, return null, else return the canonical route URL as a string:

$variables['url'] = $node->isNew() ? NULL : $node->toUrl('canonical')->toString();

Tried to help others out by sharing this solution back in a comment on the media template documentation:

Get path or URL for media entity in Twig template

mlncn commented less than a minute ago

While content entities in general have the concept of a canonical path, Drupal has failed to do themers a solid and just have it available in a consistent place. Whereas core’s template_preprocess_node provides node.html.twig a convenient {{ url }} variable:

That same helpful variable is not available in this media template. I think core should add it, and on any given project we could add it in template preprocess, but for a twig-only solution use:

{{ path('', {'media':}) }}

(Or substitute url for the full URL instead of the relative path.)

Note that this will go to the edit page by default but the view page if the standalone URL option is enabled.

See also: