37

Say I am extending The blockBase class to create a custom block, and I am implementing the blockBuild method to create some markup for my block. Something like this:

 class YourModuleBlock extends BlockBase {

   /**
     * Implements \Drupal\block\BlockBase::blockBuild().
     */
     public function build() {
      return array(
        '#markup' => 'This is a block!',
      );
     }
   }

If I wanted to provide a link in this markup, in D7 I would have used the l function, but this is no longer available (see here). So, I need to make available the new D8 method for generating links. I could just use an <a> tag, but this was never best practice in D7.

So, what is the right method to use for creating links and how do I make it available to my function?

1

10 Answers 10

69

\Drupal::l is deprecated. Maybe this case will be useful for somebody

  use Drupal\Core\Url;
  use Drupal\Core\Link;
  $url = Url::fromRoute('entity.node.edit_form', array('node' => NID));
  $project_link = Link::fromTextAndUrl(t('Open Project'), $url);
  $project_link = $project_link->toRenderable();
  // If you need some attributes.
  $project_link['#attributes'] = array('class' => array('button', 'button-action', 'button--primary', 'button--small'));
  print render($project_link);
2
  • 2
    This toRenderable() trick is very useful, thanks!
    – Nic
    Aug 30, 2016 at 13:03
  • Still works on Drupal 8.4 core
    – podarok
    May 23, 2018 at 9:31
38

Here are some examples of creating links in Drupal 8. Note that $this->t('some text') is available within blocks that extend BlockBase. If you copy these to another class that doesn't have it or use these in a .module file, you may need to change this to t() 1.

Basic Link to a node:

$node = Node::load($nid);
$build['node_link'] = $node->toLink()->toRenderable();

This creates a render array like this:

$link = [
  '#type' => 'link',
  '#url' => $url_object,
  '#title' => 'Title of Node',
];

You can create the render array without loading the node this way:

$url_object = Url::fromRoute('entity.node.canonical', ['node' => $nid]);
$link = [
  '#type' => 'link',
  '#url' => $url_object,
  '#title' => $this->t('Read More'),
];

Or using the core Link class:

$url = Url::fromRoute('entity.node.canonical', ['node' => $nid]);
$link = Link::fromTextAndUrl($this->t('Read more'), $url);
$build['read_more'] = $link->toRenderable();

If you want to use markup in the text of your link, you can't just put a string in. You need to use a render array element:

$url = Url::fromRoute('entity.node.canonical', ['node' => $nid]);
$link_text =  [
  '#type' => 'html_tag',
  '#tag' => 'span',
  '#value' => $this->t('Load More'),
];
$link = Link::fromTextAndUrl($link_text, $url);

To create an absolute link, you add this option to the URL, not the link:

$url = Url::fromRoute('entity.node.canonical', ['node' => $nid], ['absolute' => TRUE]);
$link = Link::fromTextAndUrl($this->t('Read more'), $url);
$build['read_more'] = $link->toRenderable();

To add a class to your link, you also need to add this to the URL, not the link:

$options = [
  'attributes' => [
    'class' => [
      'read-more-link',
    ],
  ],
];
$url = Url::fromRoute('entity.node.canonical', ['node' => $nid], $options);
$link = Link::fromTextAndUrl($this->t('Read more'), $url);
$build['read_more'] = $link->toRenderable();

To add a query string to your link, you also need to this to the URL, not the link:

$options = [
  'query' => [
    'car' => 'BMW',
    'model' => 'mini-cooper',
  ],
  'attributes' => [
    'class' => [
      'read-more-link',
    ],
  ],
];
$url = Url::fromRoute('entity.node.canonical', ['node' => $nid], $options);
$link = Link::fromTextAndUrl($this->t('Read more'), $url);
$build['read_more'] = $link->toRenderable();

To set the link to open in a new window with target = _blank:

$options = [
  'attributes' => [
    'target' => '_blank'
  ],
];
$url = Url::fromRoute('entity.media.edit_form', ['media' => $entity->id()], $options);
$link = Link::fromTextAndUrl(t('Edit'), $url);
$form['entity']['edit_link'] = $link->toRenderable();

Here's a link to a taxonomy term page:

$url = Url::fromRoute('entity.taxonomy_term.canonical', ['taxonomy_term' => $tid]);
$link = Link::fromTextAndUrl($this->t('Read more'), $url);
$build['read_more'] = $link->toRenderable();

Here's a link to node edit page:

$url = Url::fromRoute('entity.node.edit_form', ['node' => $nid]);
$link = Link::fromTextAndUrl($this->t('Edit'), $url);
$build['read_more'] = $link->toRenderable();

To create an external link:

$url = Url::fromUri('http://www.example.com/');
$link = Link::fromTextAndUrl($this->t('Vist this example site'), $url);
$build['external_link'] = $link->toRenderable();

Link to homepage:

$url = Url::fromRoute('<front>');
$link = Link::fromTextAndUrl($this->t('Home'), $url);
$build['homepage_link'] = $link->toRenderable();

Note that on any of these url objects, you can get the url as a string by calling:

$url->toString();

For example:

$url_string = Url::fromRoute('<front>')->toString();
3
  • The link method is deprecated.
    – Eyal
    Mar 8, 2017 at 20:11
  • I have revised my answer to use toLink() from the node object, i.e. $node->toLink()->toRenderable();
    – oknate
    Jul 19, 2017 at 16:17
  • 1
    This is a good run down.
    – Brady
    Oct 15, 2017 at 0:03
27

The change notice, l() and url() are removed in favor of a routing based URL generation API give the following code examples for Drupal 8.


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

// Internal path, defined by a route
$url = Url::fromRoute('book.admin');
$internal_link = Link::fromTextAndUrl(t('Book admin'), $url)->toString();

// External URI.
$url = Url::fromUri('http://www.example.com/');
$external_link = Link::fromTextAndUrl(t('External link'), $url)->toString();

Route names are in the .routing.yml files and (by default) in the router database table.

3
23

Another option is to create a link in a render array

$url = Url::fromRoute('entity.node.edit_form', array('node' => NID));
$link = [
  '#type' => 'link',
  '#url' => $url,
  '#title' => t('This link was rendered')
];

Drupal supplies us with some helper methods to create urls and links to entities.

$url = Node::load(NID)->toUrl('edit-form');

And

$link = Node::load(NID)->toLink(t('link text'), 'edit-form');
$link_render_array = $link->toRenderable();
3
16

The ability to set attributes seems lost or convoluted in the previous examples because the functionality is just not obvious. There are two ways to create links depending on if it has a route or not and each behaves a little bit different so here are the examples.

  1. With a route. This makes the most sense, you can just supply it in the options.

    Link::createFromRoute('My link', 
      'entity.node.canonical',
      ['node' => 123],
      ['attributes' => ['class' => 'special']]));
    
  2. With an external url. This one is a little weird. There isn't an option argument so it might seem impossible but its actually OK. The reason is that inexplicably links never have options only the URL they describe. This means you pass in the class to the URL when you create it and it will just work.

    Link::fromTextAndUrl('My link', 
      Url::FromUrl('https://example.com/about',
        ['attributes' => ['class' => 'special']]));
    

    A corollary to all this is that you can also do this.

    $link = Link::fromTextAndUrl('Example',  Url::fromUri('https://example.com/about'));
    $link->getUrl()->setOption('attributes', ['class' => 'superspecial']);
    
2

A complete example with attributes and HTML markup in the link text:

  $url = Url::fromRoute(
   'your.route.name', 
   [], 
   ['attributes' => ['id' => 'add-link', 'class' => ['btn', 'btn-sm', 'btn-primary']]]
  );
  $link = Link::fromTextAndUrl(
    Markup::create('<span class=\'glyphicon glyphicon-plus\'></span> ' . t('Add new item')), 
    $url
  );
1

The answers here gave me some good pointers. All I wanted was to provide a link to the node in my logs...So here's what I ended up with

  use Drupal\Core\Url;
  use Drupal\Core\Link;
  /* ...
  .. */  
  $url = Url::fromRoute('entity.node.canonical', array('node' => $object->id()));
$strings = array(
  '!node' => Link::fromTextAndUrl($object->getTitle(), $url)->toString(),
  '%nid' => $nid,
);
\Drupal::logger('mymodule_actions')->notice('Updating !node (%nid)', $strings);
1

For Drupal 8 or 9, if you want to add some markup around the text, you can do like this:

// If you know the route name.
$url = Url::fromRoute('route_name');
// If you just know the path.
$url = Url::fromUserInput('/yourpath');
// Create a link with HTML tag around the text.
$button = Link::fromTextAndUrl(Markup::create('<span>My page</span>'), $url);
$button = $button->toRenderable();
$button['#attributes'] = ['class' => ['class1','class2',]];
0

I was need to add link to the table as a #suffix, but was need to put it as a html, to Achieve this I did

\Drupal\Core\Link::fromTextAndUrl("Add New page", Url::fromRoute('mymodule.add_new_page'))->toString();

Where mymodule.add_new_page is route from my module yml file.

-3
\Drupal\Core\Link::createFromRoute(t('TEXT'), 'ROUTE')->toString()
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.