Add a class to a link in Drupal 8

Link example in Drupal 8The link function l() in Drupal 7 has been replaced in Drupal 8 with an object oriented approach. Adding a query and class to the link is no longer obvious.

Drupal 7

The l() function in Drupal 7 uses the third parameter for adding a query and a class to the link.

$options = [
  'query' => ['page' => 2],
  'attributes' => ['class' => ['my-link-class']],
];
$content[] = [
  '#markup' => l('Title', '/link', $options),

Drupal 8

In Drupal 8 the Link class method fromTextAndUrl() is used which takes the Url class as one of its argument.  But it is not obvious how to add a query to the URL nor how to add a class to the <a> tag. First, set up a custom module link_module by creating a link_module.info.yml in the module/custom/link_module directory.

type: module
name: Link module
description: 'Link module'
core: 8.x

Then enable the custom module. Now, create a route with the file link_module.routing.yml.

link_module.page:
  path: '/link'
  defaults:
    _controller: '\Drupal\link_module\Controller\LinkModule::content'
    _title: 'Link Example'
  requirements:
    _permission: 'access content'

And the controller in src/Controller/LinkModule.php

<?php

namespace Drupal\link_module\Controller;

use Drupal\Core\Controller\ControllerBase;

/**
 * Defines LinkModule class.
 */
class LinkModule extends ControllerBase {

  /**
   * Display the markup.
   *
   * @return array
   *   Return markup array.
   */
  public function content() {
    $content = [
      '#markup' => 'Place for my link.',
    ];

    return $content;
  }

}

Now it should be possible to enter "/link" into the browser and bring up a page with the title "Link Example" and content "Place for my link."

Now moving on to generating a link. Add the following code with the content array replacing the code for the content array above.

use Drupal\Core\Url;
use Drupal\Core\Link;
...
  $url = Url::fromRoute('');
  $link = Link::fromTextAndUrl(t('Title'), $url);

  $content = [
    '#markup' => $link->toString(),
  ];

This returns the HTML for a link to the current page

<a href="/link">Title</a>

So now there is a link but it is not obvious how to add a query string to the path nor how to add a class to the <a> element. Start with adding the query "?page=2" to the URL. The options parameter is used and documentation for this parameter is found under the function fromURI(). The documentation does not seem to be helpful since it suggests using the "query" key for the query and the "attributes" key to add a class. It does suggest using the Link class to get the added class to work. Here is an example to actually add the query where the Url:fromRoute() line replaces the previous one.

$options[] = [
  'page' => 2,
];

$url = Url::fromRoute('', $options);

Now the link has the query added.

<a href="/link?page=2">Title</a>

Note that this differs from the documentation which suggests the use of the "query" key. The "attributes" key does not seem to work as documented. Instead the Drupal render array is used to add the class. The trick is to switch from the toString() member function to the toRenderable() member function. Edit the code above to add the following the lines with the link variable and content array replaced.

$class[] = 'my-link-class';

$link = Link::fromTextAndUrl(t('Title'), $url)->toRenderable();
$link['#attributes'] = ['class' => $class];

$content = [
  '#markup' => render($link),
];

After editing the code it should be as follows.

<?php

namespace Drupal\link_module\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Url;
use Drupal\Core\Link;

/**
 * Defines LinkModule class.
 */
class LinkModule extends ControllerBase {

  /**
   * Display the markup.
   *
   * @return array
   *   Return markup array.
   */
  public function content() {
    $options[] = [
      'page' => 2,
    ];
    $class[] = 'my-link-class';

    $url = Url::fromRoute('<current>', $options);
    $link = Link::fromTextAndUrl(t('Title'), $url)->toRenderable();
    $link['#attributes'] = ['class' => $class];

    $content = [
      '#markup' => render($link),
    ];

    return $content;
  }

}

The <a> tag now has the class.

<a href="/link?page=2" class="my-link-class">Title</a>

This approach is different than suggested by the approach in the documentation. The documentation for Drupal 8 appears to be correct for the Drupal 7 l() function and perhaps is no longer right for the Drupal 8 Url class. A working approach for Drupal 8 is not obvious from the documentation and takes a lot of guesswork.

Categories