diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php index 416d802..8c5de5b 100644 --- a/core/lib/Drupal.php +++ b/core/lib/Drupal.php @@ -563,6 +563,8 @@ public static function linkGenerator() { * The link text for the anchor tag. * @param \Drupal\Core\Url $url * The URL object used for the link. + * @param array|\Drupal\Core\Template\Attribute $attributes + * The Attribute object or array of attributes applied to the link. * * @return \Drupal\Core\GeneratedLink * A GeneratedLink object containing a link to the given route and @@ -571,8 +573,8 @@ public static function linkGenerator() { * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate() * @see \Drupal\Core\Url */ - public static function l($text, Url $url) { - return static::getContainer()->get('link_generator')->generate($text, $url); + public static function l($text, Url $url, $attributes = []) { + return static::getContainer()->get('link_generator')->generate($text, $url, $attributes); } /** diff --git a/core/lib/Drupal/Core/GeneratedLink.php b/core/lib/Drupal/Core/GeneratedLink.php index a612f66..b997721 100644 --- a/core/lib/Drupal/Core/GeneratedLink.php +++ b/core/lib/Drupal/Core/GeneratedLink.php @@ -32,7 +32,7 @@ class GeneratedLink extends BubbleableMetadata implements MarkupInterface, \Coun * @return string */ public function getGeneratedLink() { - return $this->generatedLink ; + return $this->generatedLink; } /** diff --git a/core/lib/Drupal/Core/Link.php b/core/lib/Drupal/Core/Link.php index f739ad1..39df6c2 100644 --- a/core/lib/Drupal/Core/Link.php +++ b/core/lib/Drupal/Core/Link.php @@ -9,6 +9,7 @@ use Drupal\Core\Render\RenderableInterface; use Drupal\Core\Routing\LinkGeneratorTrait; +use Drupal\Core\Template\Attribute; /** * Defines an object that holds information about a link. @@ -35,16 +36,30 @@ class Link implements RenderableInterface { protected $url; /** + * The attributes for the link. + * + * @var array + */ + protected $attributes; + + /** * Constructs a new Link object. * * @param string $text * The text of the link. * @param \Drupal\Core\Url $url * The url object. + * @param array|\Drupal\Core\Template\Attribute $attributes + * The Attribute object or array of attributes applied to the link. */ - public function __construct($text, Url $url) { + public function __construct($text, Url $url, $attributes = []) { $this->text = $text; $this->url = $url; + // Store as array. + if ($attributes instanceof Attribute) { + $attributes = $attributes->toArray(); + } + $this->attributes = $attributes; } /** @@ -86,11 +101,13 @@ public static function createFromRoute($text, $route_name, $route_parameters = a * The text of the link. * @param \Drupal\Core\Url $url * The Url to create the link for. + * @param array|\Drupal\Core\Template\Attribute $attributes + * The Attribute object or array of attributes applied to the link. * * @return static */ - public static function fromTextAndUrl($text, Url $url) { - return new static($text, $url); + public static function fromTextAndUrl($text, Url $url, $attributes = []) { + return new static($text, $url, $attributes); } /** @@ -138,6 +155,33 @@ public function setUrl(Url $url) { } /** + * Returns the text of the link. + * + * @return array + */ + public function getAttributes() { + return $this->attributes; + } + + /** + * Sets the new text of the link. + + * @param array|\Drupal\Core\Template\Attribute $attributes + * The Attribute object or array of attributes applied to the link. + * + * @return $this + */ + public function setAttributes($attributes = []) { + // Store as array. + if ($attributes instanceof Attribute) { + $attributes = $attributes->toArray(); + } + $this->attributes = $attributes; + return $this; + } + + + /** * Generates the HTML for this Link object. * * @return \Drupal\Core\GeneratedLink @@ -158,6 +202,7 @@ public function toRenderable() { '#type' => 'link', '#url' => $this->url, '#title' => $this->text, + '#attributes' => $this->attributes, ]; } diff --git a/core/lib/Drupal/Core/Render/Element/Link.php b/core/lib/Drupal/Core/Render/Element/Link.php index 8f908d2..9d0f9fa 100644 --- a/core/lib/Drupal/Core/Render/Element/Link.php +++ b/core/lib/Drupal/Core/Render/Element/Link.php @@ -82,7 +82,7 @@ public static function preRenderLink($element) { $options = NestedArray::mergeDeep($element['#url']->getOptions(), $element['#options']); /** @var \Drupal\Core\Utility\LinkGenerator $link_generator */ $link_generator = \Drupal::service('link_generator'); - $generated_link = $link_generator->generate($element['#title'], $element['#url']->setOptions($options)); + $generated_link = $link_generator->generate($element['#title'], $element['#url']->setOptions($options), $element['#options']['attributes']); $element['#markup'] = $generated_link->getGeneratedLink(); $generated_link->merge(BubbleableMetadata::createFromRenderArray($element)) ->applyTo($element); diff --git a/core/lib/Drupal/Core/Template/TwigExtension.php b/core/lib/Drupal/Core/Template/TwigExtension.php index 693da9a..c9089d9 100644 --- a/core/lib/Drupal/Core/Template/TwigExtension.php +++ b/core/lib/Drupal/Core/Template/TwigExtension.php @@ -275,12 +275,12 @@ public function getLink($text, $url, $attributes = []) { if ($existing_attributes = $url->getOption('attributes')) { $attributes = array_merge($existing_attributes, $attributes); } - $url->setOption('attributes', $attributes); } $build = [ '#type' => 'link', '#title' => $text, '#url' => $url, + '#attributes' => $attributes, ]; return $build; } diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php index 70af473..3163119 100644 --- a/core/lib/Drupal/Core/Utility/LinkGenerator.php +++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php @@ -80,7 +80,13 @@ public function generateFromLink(Link $link) { * * @see system_page_attachments() */ - public function generate($text, Url $url) { + public function generate($text, Url $url, $attributes = []) { + if (!($attributes instanceof Attribute)) { + $attributes = new Attribute($attributes); + } + // Include a placeholder for the href. + $attributes['href'] = ''; + // Performance: avoid Url::toString() needing to retrieve the URL generator // service from the container. $url->setUrlGenerator($this->urlGenerator); @@ -148,8 +154,17 @@ public function generate($text, Url $url) { $this->moduleHandler->alter('link', $variables); // Move attributes out of options since generateFromRoute() doesn't need - // them. Include a placeholder for the href. - $attributes = array('href' => '') + $variables['options']['attributes']; + // them. + foreach ($variables['options']['attributes'] as $attribute_name => $attribute_value) { + // Merge classes. + if ($attribute_name == 'class') { + $attributes->addClass($attribute_value); + } + elseif (!isset($attributes[$attribute_name])) { + // Set from options if not already provided on $attributes. + $attributes[$attribute_name] = $attribute_value; + } + } unset($variables['options']['attributes']); $url->setOptions($variables['options']); @@ -169,7 +184,7 @@ public function generate($text, Url $url) { if (!SafeMarkup::isSafe($variables['text'])) { $variables['text'] = Html::escape($variables['text']); } - $attributes = new Attribute($attributes); + // This is safe because Attribute does escaping and $variables['text'] is // either rendered or escaped. return $generated_link->setGeneratedLink('' . $variables['text'] . ''); diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php index 8029d70..13939ea 100644 --- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php +++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php @@ -60,6 +60,8 @@ * class will be applied to the link. It is important to use this * sparingly since it is usually unnecessary and requires extra * processing. + * @param array|\Drupal\Core\Template\Attribute $attributes + * The Attribute object or array of attributes applied to the link. * * @return \Drupal\Core\GeneratedLink * A GeneratedLink object containing a link to the given route and @@ -76,7 +78,7 @@ * @internal * Should not be used in user code. Use \Drupal\Core\Link instead. */ - public function generate($text, Url $url); + public function generate($text, Url $url, $attributes); /** * Renders a link from a link object. diff --git a/core/tests/Drupal/Tests/Core/DrupalTest.php b/core/tests/Drupal/Tests/Core/DrupalTest.php index d22ed78..db59ca3 100644 --- a/core/tests/Drupal/Tests/Core/DrupalTest.php +++ b/core/tests/Drupal/Tests/Core/DrupalTest.php @@ -357,11 +357,11 @@ public function testL() { $url = new Url('test_route', $route_parameters, $options); $generator->expects($this->once()) ->method('generate') - ->with('Test title', $url) + ->with('Test title', $url, ['class' => ['button']]) ->will($this->returnValue('link_html_string')); $this->setMockContainerService('link_generator', $generator); - $this->assertInternalType('string', \Drupal::l('Test title', $url)); + $this->assertInternalType('string', \Drupal::l('Test title', $url, ['class' => ['button']])); } /**