Não encontrei uma maneira de alterar o link '#markup' no galho, mas há uma maneira de alterá-lo na fase de renderização.
Criei este pequeno módulo que estende a funcionalidade Link e permite injetar algumas coisas no link renderizado. Então vamos fazer um código, vou explicar nos comentários ...
Estrutura de arquivo do módulo:
better_link
| - src
| - Element
| BetterLink.php
| - Plugin
| - FieldFormatter
| BetterLinkFormatter.php
| better_link.info.yml
| better_link.module
Conteúdo do arquivo:
better_link.info.yml
name: 'Better link'
type: module
package: 'Field types'
description: 'A very nice better link'
core: '8.x'
dependencies:
- field
- link
better_link.module
<?php
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
* Just some words about the module.
*/
function better_link_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
case 'help.page.better_link':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Provide a improved link formatter and renderer for a custom link markup.') . '</p>';
$output .= '<p>' . t('Will be added a span html tag right before link content.') . '</p>';
$output .= '<p>' . t(' - Link class can be added throught manage display.') . '</p>';
$output .= '<p>' . t(' - Span class can be added throught manage display.') . '</p>';
return $output;
}
}
BetterLinkFormatter.php
<?php
/**
* @file
* Contains \Drupal\better_link\Plugin\Field\FieldFormatter\BetterLinkFormatter.
*/
namespace Drupal\better_link\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Form\FormStateInterface;
use Drupal\link\Plugin\Field\FieldFormatter\LinkFormatter;
/**
* Plugin implementation of the 'better_link' formatter.
*
* @FieldFormatter(
* id = "better_link",
* label = @Translation("Better Link"),
* field_types = {
* "link"
* }
* )
*/
class BetterLinkFormatter extends LinkFormatter {
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
$settings = parent::defaultSettings();
//Keeping simple...
$settings['span_class'] = '';
$settings['link_class'] = '';
//... but feel free to add, tag_name, buble_class, wraper_or_inside
return $settings;
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$form = parent::settingsForm($form, $form_state);
//Make sure that you always store a name that can be used as class
$settings['link_class'] = Html::cleanCssIdentifier(Unicode::strtolower($this->getSetting('link_class')));
$settings['span_class'] = Html::cleanCssIdentifier(Unicode::strtolower($this->getSetting('span_class')));
$this->setSettings($settings);
$form['link_class'] = array(
'#title' => $this->t('Inject this class to link'),
'#type' => 'textfield',
'#default_value' => $settings['link_class'],
);
$form['span_class'] = array(
'#title' => $this->t('Inject this class to span'),
'#type' => 'textfield',
'#default_value' => $settings['span_class'],
);
return $form;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$summary = parent::settingsSummary();
//Same here. Somehow if you use setSettings here don't reflect in settingsForm
$settings['link_class'] = Html::cleanCssIdentifier(Unicode::strtolower($this->getSetting('link_class')));
$settings['span_class'] = Html::cleanCssIdentifier(Unicode::strtolower($this->getSetting('span_class')));
$this->setSettings($settings);
//Summary is located in the right side of your field (in manage display)
if (!empty($settings['link_class'])) {
$summary[] = t("Class '@class' will be used in link element.", array('@class' => $settings['link_class']));
}
else {
$summary[] = t('No class is defined for link element.');
}
if (!empty($settings['span_class'])) {
$summary[] = t("Class '@class' will be used in span element.", array('@class' => $settings['span_class']));
}
else {
$summary[] = t('No class is defined for span element.');
}
return $summary;
}
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $items, $langcode) {
$elements = parent::viewElements($items, $langcode);
//Yeah, here too, same 'problem'.
$settings['link_class'] = Html::cleanCssIdentifier(Unicode::strtolower($this->getSetting('link_class')));
$settings['span_class'] = Html::cleanCssIdentifier(Unicode::strtolower($this->getSetting('span_class')));
foreach ($items as $delta => $item) {
//Lets change the render element type and inject some options that will
//be used in render phase
if (isset($elements[$delta]['#type'])) {
$elements[$delta]['#type'] = 'better_link';
$elements[$delta]['#options']['#link_class'] = $settings['link_class'];
$elements[$delta]['#options']['#span_class'] = $settings['span_class'];
}
}
//Next step, render phase, see ya...
return $elements;
}
}
BetterLink.php
<?php
/**
* @file
* Contains \Drupal\better_link\Element\BetterLink.
*/
namespace Drupal\better_link\Element;
use Drupal\Core\Render\Element\Link;
/**
* Provides a better_link render element. Almost the same as link.
*
* @RenderElement("better_link")
*/
class BetterLink extends Link {
/**
* {@inheritdoc}
*/
public function getInfo() {
$class = get_class($this);
return array(
'#pre_render' => array(
array($class, 'preRenderLink'),
),
);
}
/**
* {@inheritdoc}
*/
public static function preRenderLink($element) {
//Hello again. Lets work.
//Before Drupal create the rendered link element lets inject our stuff...
//...Our class to link
$element['#options']['attributes']['class'][] = $element['#options']['#link_class'];
//...Build span classes
$span_classes = $element['#options']['#span_class'] . ' ' . $element['#options']['#link_class'];
//...And get rid them.
unset($element['#options']['#link_class']);
unset($element['#options']['#span_class']);
//Lets Drupal do the hard work
$element = parent::preRenderLink($element);
//Here is where the magic happens ;)
if (!empty($element['#markup'])) {
//Inject our span right before link content.
$element['#markup'] = str_replace('">', "\"><span class='$span_classes'></span>", $element['#markup']);
//Side comment - Thank you spaceless, str_replace can be used here
}
//Now, whatever you change in your url or another object will not maintain,
//the only thing that will be returned in the end is
//$element['#markup'], so this is the only thing you can change.
return $element;
}
}
Importante:
Isso funcionará para todos os seus campos de link , com certeza, se você alterar o formatador na exibição de gerenciamento (editando o tipo de nó).
Espero que possa ser útil.
Pedido para @artfulrobot: Você pode testar este módulo? Eu acho que esse problema de tradução pode ser resolvido dessa maneira.