twigでリンク「#markup」を変更する方法は見つかりませんが、レンダリング段階で変更する方法はあります。
私はこの小さなモジュールを作成して、リンク機能を拡張し、レンダリングされたリンクにいくつかのものを挿入できるようにしました。だからいくつかのコードをやってみましょう、コメントで説明します...
モジュールファイル構造:
better_link
| - src
| - Element
| BetterLink.php
| - Plugin
| - FieldFormatter
| BetterLinkFormatter.php
| better_link.info.yml
| better_link.module
ファイルの内容:
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;
}
}
重要:
これは、表示の管理(ノードタイプの編集)でフォーマッタを変更すると、すべてのリンクフィールドで機能します。
お役に立てば幸いです。
@artfulrobotへのリクエスト:このモジュールをテストできますか?翻訳の問題はこの方法で解決できると思います。