Sorry, you need to enable JavaScript to visit this website.
Skip to main content
Welcome to our website! Explore our services and portfolio.

Converting Routes or Raw URLs into Full URI and Link Objects in Drupal

Submitted by admin on

In Drupal, the standard way to handle links is through the Url and Link classes. These provide a robust wrapper around paths that handles aliasing, permissions, and language prefixes automatically.1

 


1. Converting a Route to a URI and Link2

If you know the route name (e.g., entity.node.canonical or user.login), use the fromRoute method.3

 

PHP

use Drupal\Core\Url;
use Drupal\Core\Link;

// 1. Create the Url object
$url = Url::fromRoute('entity.node.canonical', ['node' => 1]);

// 2. Convert to a string (URI)
$uri_string = $url->toString(); // e.g., "/node/1" or "/alias-name"

// 3. Create a Link object
$link = Link::fromTextAndUrl($this->t('View Node'), $url);

// 4. Get the HTML string
$link_html = $link->toString(); // e.g., <a href="/node/1">View Node</a>

2. Converting Raw URLs or URIs

If you have a raw string (like https://google.com or /admin/content), use the fromUri or fromUserInput methods.

Method

Use Case

Example

fromUri()

External or internal system URIs.

Url::fromUri('https://google.com')

fromUserInput()

Relative paths starting with /, ?, or #.

Url::fromUserInput('/admin/people')

fromEntity()

When you have an entity object.

$node->toUrl()


3. Working with Link Render Arrays

In Drupal, you should avoid printing raw HTML strings whenever possible. Instead, convert your Link or Url object into a Render Array.4 This allows other modules to alter the link before it's displayed.

 

PHP

// Convert a Link object to a render array
$build['my_link'] = $link->toRenderable();

// Add attributes (like classes) to the link
$url->setOption('attributes', ['class' => ['button', 'button--primary']]);
$build['styled_link'] = [
  '#type' => 'link',
  '#title' => $this->t('Click Me'),
  '#url' => $url,
];

4. Helpful "Magic" Routes

Drupal provides special route names for common tasks:

  • <front>: The site's front page.5

     

  • <current>: The current page.

  • <none>: Used for menu items that shouldn't link anywhere (often used for parent containers).

Common Example: Linking to a Node

PHP

$node = \Drupal\node\Entity\Node::load(1);
$link = $node->toLink($this->t('Read more'))->toRenderable();

Next we will see how to check if a user has access to a specific route before you generate the link.


 

Checking for access before rendering a link is a best practice in Drupal. It prevents users from seeing "Access Denied" pages and improves the UX by simply hiding links they can't use.

The most direct way to do this is using the access() method on a Url object or by using the Access Manager service.


1. Using the Url Object

If you already have a Url object, you can simply call its access() method. By default, it checks access for the current user.

PHP

use Drupal\Core\Url;

$url = Url::fromRoute('entity.node.edit_form', ['node' => 1]);

if ($url->access()) {
  // Generate and display the link.
  $link = [
    '#type' => 'link',
    '#title' => $this->t('Edit Content'),
    '#url' => $url,
  ];
}

2. Using the Access Manager Service

If you want to check access for a different user or if you don't want to instantiate a full Url object yet, use the access_manager service.

PHP

$access_manager = \Drupal::service('access_manager');
$current_user = \Drupal::currentUser();

// Check if the user can access a specific route.
$can_access = $access_manager->checkNamedRoute(
  'entity.node.canonical', 
  ['node' => 5], 
  $current_user
);

if ($can_access) {
  // Perform logic...
}

3. Checking Entity Access Directly

If your link points to an entity (like a Node or Term), you can check the entity's own access handler. This is often more performant than a full route check.

PHP

$node = \Drupal\node\Entity\Node::load(1);

// Operations can be 'view', 'update', or 'delete'.
if ($node->access('update')) {
  // Show the "Edit" link.
}

Summary of Methods

Approach

Best Use Case

$url->access()

Quick check for the current user when you already have a Url object.

AccessManager

When checking access for a different user or bulk-checking routes.

$entity->access()

When the link is specifically for a CRUD operation on an entity.

Note on Cacheability

When checking access programmatically, Drupal automatically tracks these checks in the render cache. If a user's permissions change, the cached links will be invalidated correctly because access() returns an AccessResult object that contains necessary cache metadata.

Next we will see how to alter existing links in a menu or a block using hook_link_alter().





 

To alter existing links globally—whether they come from a menu, a block, or a field—you can use hook_link_alter(). This hook allows you to intercept the $variables array right before the link is rendered.

It is particularly useful for adding CSS classes, appending query parameters, or changing link text dynamically across the entire site.


1. Implementing hook_link_alter()

Add this function to your .module file.

PHP

/**
 * Implements hook_link_alter().
 */
function my_module_link_alter(&$variables) {
  // 1. Target links by their route name
  if (isset($variables['url']) && $variables['url']->isRouted()) {
    $route_name = $variables['url']->getRouteName();

    // Example: Add a specific class to all "User Login" links
    if ($route_name === 'user.login') {
      $variables['options']['attributes']['class'][] = 'login-popup-trigger';
    }
    
    // Example: Add a warning to all admin links for non-admin users
    if (strpos($route_name, 'admin') !== FALSE) {
      $variables['text'] = t('@text (Admin only)', ['@text' => $variables['text']]);
    }
  }

  // 2. Target links by their URL options (e.g. adding a "rel" attribute to external links)
  if (isset($variables['url']) && $variables['url']->isExternal()) {
    $variables['options']['attributes']['rel'] = 'nofollow noopener';
    $variables['options']['attributes']['target'] = '_blank';
  }
}

2. Common Altering Use Cases

Target

Modification Method

Link Text

Modify $variables['text']. (Use t() to keep it translatable).

Classes

Append to $variables['options']['attributes']['class'][].

Query Params

Set $variables['options']['query']['key'] = 'value'.

Fragment

Set $variables['options']['fragment'] = 'section-id'.


3. Alternative: hook_menu_links_discovered_alter()

If you only want to change statically defined menu items (those defined in .links.menu.yml files) before they are even built into a menu tree, use this hook instead. It is more performant for structural menu changes.

PHP

/**
 * Implements hook_menu_links_discovered_alter().
 */
function my_module_menu_links_discovered_alter(&$links) {
  // Change the title of the standard 'Logout' link
  if (isset($links['user.logout'])) {
    $links['user.logout']['title'] = t('Exit System');
    $links['user.logout']['weight'] = 100;
  }
}

Important: Caching Considerations

Because hook_link_alter() runs during the rendering process, any logic inside it that depends on the current user or state must be accompanied by the correct Cache Contexts.

If you change a link based on a user's role, Drupal needs to know that the cached version of that link is only valid for users with that specific role. In hook_link_alter(), you can't easily add cache metadata directly to the $variables array, so it's often better to ensure your route or block already has the correct user.roles cache context.



For additional later discussion - how to create a Menu Link Plugin class if you need the link title to be truly dynamic (like "Hello, [User Name]").