Como tema um link de um arquivo de modelo?

10

Um modelo de galho está renderizando uma lista de links que acompanham as classes. O básico:

{{ mylink }}

código galho produzirá algo como

<a href="#" class="someclass" >the text</a>

Nem todos os links têm classes. Eu quero escrever um modelo de galho que, em vez disso, produzirá algo como isto:

<a href="#" class="someclass" >
  <span class="sprite someclass" ></span>
  the text</a>

O que eu tentei:

  1. Procurei o modelo de galho para substituir. Infelizmente, parece que os links não são renderizados por um modelo de galho.

  2. Eu tentei atualizar a variável galho como

    set mylink['#title'] = "<span>...</span>" ~ mylink['#title']

    Mas não vai me deixar fazer isso.

artfulrobot
fonte
Deve ser apenas no modelo de galho? Sou capaz de alterar a marcação e definir classes na interface do usuário (tipo de conteúdo> gerenciar formulário de exibição).
Vagner

Respostas:

6

Aqui está uma solução única para um campo específico que precisa desse tratamento; não é uma solução genérica para todos os links em qualquer lugar.

some-template.twig:

<ul class="field--name-field-links">
  {% for item in content.field_links %}
  {% if item['#title'] %}
    <li>
      <a href="{{ item['#url'] }}" class="{{ item['#options'].attributes.class|join(' ') }}" >
        {% if item['#options']['attributes']['class'] %}
          <span class="sprite {{ item['#options']['attributes']['class']|join(" ") }}"></span>
        {% endif %}
        {{ item['#title'] }}
      </a>
    </li>
  {% endif %}
  {% endfor %}
</ul>
artfulrobot
fonte
1
OMG finalmente, estou procurando há 2 dias uma solução para este problema. Ainda não consigo entender como o twig está gerando html quando passamos o item.link, que é uma matriz. Alguém tem um documento para isso?
Guillaume Bois
Oh jorra ... Infelizmente esta solução funciona apenas parcialmente. Quero alterar os links do alternador de idiomas e usar item.link['#url']o mesmo URL para todos os idiomas!
Guillaume Bois
@GuillaumeBois Você pode testar drupal.stackexchange.com/a/199998/54619 para ver se resolve o problema do 'comutador de idioma'? Obrigado
Vagner
5

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.

Vagner
fonte
Sim, obrigado por uma resposta longa e detalhada. Eu acho que há uma falha maciça na camada de galhos do d8 com enormes soluções baseadas em php para o que deveria ser uma questão temática simples. Mas obrigado por postar, v útil.
Artfulrobot 3/16
@artfulrobot Você provavelmente está em uma posição melhor para responder a isso do que eu - qual das respostas aqui você acha que a recompensa deve ir?
Clive
@ Clive obrigado, mas sua recompensa, sua ligação. A pergunta que fiz foi sobre Twig. A maioria dessas respostas envolve a substituição ou ampliação do núcleo por um monte de PHP mais difícil de manter, portanto, embora eu seja grato pela contribuição e forneça maneiras de realizar o trabalho, eles não respondem à pergunta IMO. Esse problema de tema "simples" tem sido a palha que quebrou as costas do d8 do meu camelo, receio e acabei abandonando um projeto do d8 há 3 meses para começar do zero em 7 - v decepcionante, mas em 1 semana eu ' apanhados completamente: - |
Artfulrobot
Obrigado @artfulrobot, entendido. É uma pena que isso não tenha uma conclusão mais satisfatória. Vou deixar que a recompensa se auto-premie sobre o que a comunidade tiver votado
Clive
galho é incrível. Todo o problema vem do sistema temático drupal, que eu acho que é uma abordagem errada. Basta verificar quanto trabalho extra você deve fazer se desejar personalizar um link simples. Isso é decepcionante.
Zoltán Süle 03/02
4

você pode adicionar uma matriz de renderização ao #title, como:

['#title'] = array('#markup' => '<i class="my-icons">yummy</i>' . $item['content']['#title']);

Antiga resposta longa:

Você pode substituir o serviço do gerador de link

Crie um módulo (alternative_linkgenerator), com um arquivo de informações alternative_linkgenerator.info.yml

name: Alternative LinkGenerator
type: module
description: Adds alternative link generation.
core: 8.x

Crie um arquivo chamado alternative_linkgenerator.services.yml

services:
  alternative_linkgenerator.link_generator:
    class: Drupal\alternative_linkgenerator\AlternativeLinkGenerator

A seguir, crie a classe, adicione uma pasta chamada "src" (seguindo os padrões de carregamento automático do PSR-4) e, dentro deste, um arquivo chamado AlternativeLinkGenerator.php. (Esta é uma cópia 1: 1, é necessário adaptar as coisas para o seu)

class AlternativeLinkGenerator extends LinkGeneratorInterface {

  /**
   * The url generator.
   *
   * @var \Drupal\Core\Routing\UrlGeneratorInterface
   */
  protected $urlGenerator;

  /**
   * The module handler firing the route_link alter hook.
   *
   * @var \Drupal\Core\Extension\ModuleHandlerInterface
   */
  protected $moduleHandler;

  /**
   * The renderer service.
   *
   * @var \Drupal\Core\Render\RendererInterface
   */
  protected $renderer;

  /**
   * Constructs a LinkGenerator instance.
   *
   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
   *   The url generator.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
   * @param \Drupal\Core\Render\RendererInterface $renderer
   *   The renderer service.
   */
  public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler, RendererInterface $renderer) {
    $this->urlGenerator = $url_generator;
    $this->moduleHandler = $module_handler;
    $this->renderer = $renderer;
  }

  /**
   * {@inheritdoc}
   */
  public function generateFromLink(Link $link) {
    return $this->generate($link->getText(), $link->getUrl());
  }

  /**
   * {@inheritdoc}
   *
   * For anonymous users, the "active" class will be calculated on the server,
   * because most sites serve each anonymous user the same cached page anyway.
   * For authenticated users, the "active" class will be calculated on the
   * client (through JavaScript), only data- attributes are added to links to
   * prevent breaking the render cache. The JavaScript is added in
   * system_page_attachments().
   *
   * @see system_page_attachments()
   */
  public function generate($text, Url $url) {
    // Performance: avoid Url::toString() needing to retrieve the URL generator
    // service from the container.
    $url->setUrlGenerator($this->urlGenerator);

    if (is_array($text)) {
      $text = $this->renderer->render($text);
    }

    // Start building a structured representation of our link to be altered later.
    $variables = array(
      'text' => $text,
      'url' => $url,
      'options' => $url->getOptions(),
    );

    // Merge in default options.
    $variables['options'] += array(
      'attributes' => array(),
      'query' => array(),
      'language' => NULL,
      'set_active_class' => FALSE,
      'absolute' => FALSE,
    );

    // Add a hreflang attribute if we know the language of this link's url and
    // hreflang has not already been set.
    if (!empty($variables['options']['language']) && !isset($variables['options']['attributes']['hreflang'])) {
      $variables['options']['attributes']['hreflang'] = $variables['options']['language']->getId();
    }

    // Ensure that query values are strings.
    array_walk($variables['options']['query'], function(&$value) {
      if ($value instanceof MarkupInterface) {
        $value = (string) $value;
      }
    });

    // Set the "active" class if the 'set_active_class' option is not empty.
    if (!empty($variables['options']['set_active_class']) && !$url->isExternal()) {
      // Add a "data-drupal-link-query" attribute to let the
      // drupal.active-link library know the query in a standardized manner.
      if (!empty($variables['options']['query'])) {
        $query = $variables['options']['query'];
        ksort($query);
        $variables['options']['attributes']['data-drupal-link-query'] = Json::encode($query);
      }

      // Add a "data-drupal-link-system-path" attribute to let the
      // drupal.active-link library know the path in a standardized manner.
      if ($url->isRouted() && !isset($variables['options']['attributes']['data-drupal-link-system-path'])) {
        // @todo System path is deprecated - use the route name and parameters.
        $system_path = $url->getInternalPath();
        // Special case for the front page.
        $variables['options']['attributes']['data-drupal-link-system-path'] = $system_path == '' ? '<front>' : $system_path;
      }
    }

    // Remove all HTML and PHP tags from a tooltip, calling expensive strip_tags()
    // only when a quick strpos() gives suspicion tags are present.
    if (isset($variables['options']['attributes']['title']) && strpos($variables['options']['attributes']['title'], '<') !== FALSE) {
      $variables['options']['attributes']['title'] = strip_tags($variables['options']['attributes']['title']);
    }

    // Allow other modules to modify the structure of the link.
    $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'];
    unset($variables['options']['attributes']);
    $url->setOptions($variables['options']);

    // External URLs can not have cacheable metadata.
    if ($url->isExternal()) {
      $generated_link = new GeneratedLink();
      $attributes['href'] = $url->toString(FALSE);
    }
    else {
      $generated_url = $url->toString(TRUE);
      $generated_link = GeneratedLink::createFromObject($generated_url);
      // The result of the URL generator is a plain-text URL to use as the href
      // attribute, and it is escaped by \Drupal\Core\Template\Attribute.
      $attributes['href'] = $generated_url->getGeneratedUrl();
    }

    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('<a' . $attributes . '>' . $variables['text'] . '</a>');
  }

}

Edite services.yml (normalmente em sites / default / services.yml na sua base de código do Drupal 8) e adicione o seguinte:

  services:
    link_generator:
      alias: alternative_linkgenerator.link_generator

adereços vai aqui

rémy
fonte
Obrigado, vou tentar. Eu realmente quero que isso seja feito em certos contextos, no entanto. Irritante ter que fazer uma coisa temática pura em php após o anúncio do galho grande. Obrigado por sua sugestão.
Artfulrobot
Essa função não parece ser chamada. Eu acho que é para "link com título separado e elementos de URL". Nem a template_preprocess_linkscoisa é chamada (isso é algo específico, apesar do nome genérico).
Artfulrobot
template pré-processar ligações é para listas de links, tanto quanto eu vejo, você sempre pode ativar galho de depuração para ver o template / função de pré-processamento foi utilizado para a saída
rémy
Sim, nenhum desses é usado para formatar links. Na verdade core/lib/Drupal/Core/Utility/LinkGenerator.php, generate()é usado e isso força o texto a ser passado, de Html::escape()modo que não há como fazê-lo sem ignorar completamente o formatador de link do Drupal.
Artfulrobot #
Você pode substituir este serviço como qualquer outro, consulte aqui tim.millwoodonline.co.uk/post/125163259445/…
rémy
0

tente este código:

{% if links -%}
  {%- if heading -%}
    {%- if heading.level -%}
  <{{ heading.level }}{{ heading.attributes }}>{{ heading.text }}</{{ heading.level }}>
{%- else -%}
  <h2{{ heading.attributes }}>{{ heading.text }}</h2>
   {%- endif -%}
  {%- endif -%}
  <ul{{ attributes }}>
{%- for item in links -%}
  <li{{ item.attributes }}>
        {%- if item.link -%}

    <!--{{ item.link }} this line must stay -->

    <a href="{{ item.link['#url'] }}"
      {{ item.attributes.addClass(classes) }}
      {{ item.attributes.setAttribute('title', item.text ) }}
      {{ item.attributes.setAttribute('lang', item.link['#options'].language.id ) }}
      {{ item.attributes.setAttribute('aria-label', item.text ) }}>
        <img alt="{{ item.link['#title'] }}" src="/themes/subtheme/img/flag_{{ item.link['#options'].language.id }}.jpg" class="align-center">
    </a>


    {%- elseif item.text_attributes -%}
      <span{{ item.text_attributes }}>{{ item.text }}</span>
    {%- else -%}
      {{ item.text }}
    {%- endif -%}
  </li>
{%- endfor -%}

{%- fim se %}

ou este (vem de: https://github.com/liip/bund_drupal_starterkit_theme/blob/master/templates/navigation/links--language-block.html.twig ):

{% if links and links|length > 1 -%}
  <ul>
    {%- for item in links -%}
      <li>
        {%- if item.link -%}

      <!--{{ item.link }} to do: remove this line without breaking the urls -->

      {% if item.link['#options'].language.id == current_language %}
        {% set classes = ['active'] %}
      {% else %}
        {% set classes = [''] %}
      {% endif %}
      {% set url = path(item.link['#url'].routeName, item.link['#url'].routeParameters, item.link['#url'].options) %}

    {%- else -%}
      {% set classes = ['disabled'] %}
      {% set url = '#' %}
    {%- endif -%}

    <a href="{{ url }}"
      {{ item.attributes.addClass(classes) }}
      {{ item.attributes.setAttribute('title', item.text ) }}
      {{ item.attributes.setAttribute('lang', item.link['#options'].language.id ) }}
      {{ item.attributes.setAttribute('aria-label', item.text ) }}>
        {{ item.link['#options'].language.id | upper }}
    </a>
  </li>
{%- endfor -%}
  </ul>
{%- endif %}
Tomek
fonte