ฉันจะจัดรูปแบบลิงก์จากไฟล์เทมเพลตได้อย่างไร


10

เทมเพลตกิ่งไม้กำลังแสดงรายการลิงก์ที่มาพร้อมกับคลาส พื้นฐาน:

{{ mylink }}

รหัสทวิจะส่งออกบางสิ่งเช่น

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

ลิงก์ไม่ได้มีคลาสทั้งหมด ฉันต้องการเขียนแม่แบบกิ่งไม้ที่จะเอาท์พุทแบบนี้แทน:

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

สิ่งที่ฉันได้ลอง:

  1. ฉันมองหาแม่แบบกิ่งไม้เพื่อแทนที่ น่าเสียดายที่ดูเหมือนว่าลิงค์จะไม่แสดงผลโดยแม่แบบกิ่งไม้

  2. ฉันพยายามอัปเดตตัวแปร twig เช่น

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

    แต่มันจะไม่ทำให้ฉันทำอย่างนั้น


มันจะต้องมีเฉพาะในแม่แบบกิ่งไม้? ฉันสามารถเปลี่ยนมาร์กอัปและตั้งค่าคลาสจาก UI (ประเภทเนื้อหา> จัดการแบบฟอร์มการแสดงผล)
Vagner

คำตอบ:


6

นี่เป็นวิธีแก้ปัญหาเฉพาะสาขาที่ต้องการการรักษานี้ มันไม่ใช่โซลูชันทั่วไปสำหรับทุกลิงก์ในทุกที่

บาง 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>

1
ในที่สุด OMG ฉันได้ค้นหาวิธีแก้ปัญหานี้มา 2 วันแล้ว ฉันยังไม่เข้าใจว่ากิ่งไม้แสดงผล html อย่างไรเมื่อเราส่ง item.link ซึ่งเป็นอาร์เรย์ ใครมีเอกสารสำหรับเรื่องนี้?
Guillaume Bois

โอ้พรั่งพรู ... โชคไม่ดีที่วิธีนี้ใช้ได้ผลเพียงบางส่วนเท่านั้น ฉันต้องการเปลี่ยนลิงค์ภาษาของ switcher และการใช้item.link['#url']นั้นให้ URL เดียวกันกับทุกภาษา!
Guillaume Bois

@GuillaumeBois คุณสามารถทดสอบdrupal.stackexchange.com/a/199998/54619เพื่อดูว่าแก้ปัญหาของ 'ตัวสลับภาษา' หรือไม่ ขอบคุณ
Vagner

5

ฉันไม่พบวิธีเปลี่ยนลิงก์ '#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: คุณสามารถทดสอบโมดูลนี้ได้หรือไม่ ฉันคิดว่าปัญหาการแปลสามารถแก้ไขได้ด้วยวิธีนี้


ใช่ขอบคุณสำหรับคำตอบที่ยาวและละเอียดมาก ฉันคิดว่ามีความล้มเหลวอย่างมากในชั้น twig ของ d8 พร้อมด้วยโซลูชั่นที่ใช้ php ขนาดใหญ่สำหรับสิ่งที่ควรจะเป็นปัญหาแบบง่ายๆ แต่ขอบคุณสำหรับการโพสต์ v เป็นประโยชน์
artfulrobot

@artfulrobot คุณน่าจะตอบคำถามได้ดีกว่าฉัน - คำตอบใดที่คุณคิดว่าควรจะได้
Clive

@clive ขอบคุณ แต่ความโปรดปรานของคุณโทรของคุณ คำถามที่ฉันถามเกี่ยวกับทวิก คำตอบเหล่านี้ส่วนใหญ่เกี่ยวข้องกับการเปลี่ยนหรือขยายคอร์ด้วย PHP ที่ยากต่อการบำรุงรักษามากมายดังนั้นในขณะที่ฉันรู้สึกขอบคุณสำหรับอินพุตและพวกเขามีวิธีในการทำงานให้เสร็จพวกเขาไม่ตอบคำถาม IMO ปัญหาเรื่องง่าย ๆ นี้เป็นฟางที่ทำลายอูฐของฉันกลับมาฉันกลัวและลงเอยด้วยการทำโครงการ d8 3 เดือนเพื่อเริ่มต้นจากศูนย์ใน 7 - v น่าผิดหวัง แต่ใน 1 สัปดาห์ฉัน d จมขึ้นอย่างสมบูรณ์: - |
artfulrobot

ขอบคุณ @artfulrobot เข้าใจ มันเป็นความอัปยศที่ไม่มีข้อสรุปที่น่าพอใจมากกว่านี้ ฉันจะให้รางวัลอัตโนมัติให้รางวัลตัวเองในทุกสิ่งที่ชุมชนโหวต
Clive

กิ่งไม้เป็นที่น่าอัศจรรย์ ปัญหาทั้งหมดมาจากระบบ drupal theming ซึ่งฉันคิดว่าเป็นวิธีที่ผิด เพียงตรวจสอบว่าคุณต้องทำงานพิเศษมากแค่ไหนหากคุณต้องการปรับแต่งลิงค์แบบง่าย ๆ มันน่าผิดหวัง
ZoltánSüle

4

คุณสามารถเพิ่มอาร์เรย์การเรนเดอร์ไปยัง #title ได้เช่น:

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

คำตอบยาวเก่า:

คุณสามารถลบล้างบริการตัวสร้างลิงค์

สร้างโมดูล (Altern_linkgenerator) พร้อมไฟล์ข้อมูล Altern_linkgenerator.info.yml

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

สร้างไฟล์ชื่อ Altern_linkgenerator.services.yml

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

ถัดไปคือการสร้างคลาสเพิ่มโฟลเดอร์ชื่อ“ src” (ตามมาตรฐานการโหลดอัตโนมัติ PSR-4) และภายในไฟล์นี้ชื่อว่า AlternativeLinkGenerator.php (นี่คือสำเนา 1: 1 คุณต้องปรับเปลี่ยนสิ่งต่าง ๆ สำหรับคุณ)

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>');
  }

}

แก้ไข services.yml (ตามปกติที่ sites / default / services.yml ในฐานข้อมูล Drupal 8 ของคุณ) และเพิ่มรายการต่อไปนี้:

  services:
    link_generator:
      alias: alternative_linkgenerator.link_generator

อุปกรณ์ประกอบฉากไปที่นี่


ขอบคุณฉันจะให้มันไป ฉันแค่อยากให้ทำในบริบทบางอย่างเท่านั้น น่ารำคาญที่จะต้องทำสิ่งที่ใจบริสุทธิ์ใน php หลังจากประกาศกิ่งใหญ่ ขอบคุณสำหรับคำแนะนำของคุณ
artfulrobot

ฟังก์ชั่นนั้นดูเหมือนจะไม่ถูกเรียก ฉันคิดว่ามีไว้สำหรับ "ลิงก์ที่มีชื่อเรื่องและองค์ประกอบ URL" ไม่ใช่template_preprocess_linksสิ่งที่ถูกเรียก (เป็นสิ่งที่เฉพาะเจาะจงแม้จะเป็นชื่อที่มีเสียงทั่วไป)
artfulrobot

ลิงก์ preprocess ของเทมเพลตใช้สำหรับรายการลิงก์เท่าที่ฉันเห็นคุณสามารถเปิดใช้งานทวิก debug เพื่อดูว่าฟังก์ชั่นเทมเพลต / preprocess ใช้สำหรับการส่งออกอย่างไร
rémy

ใช่ไม่ใช้สำหรับการจัดรูปแบบลิงก์ อันที่จริงcore/lib/Drupal/Core/Utility/LinkGenerator.php's generate()ถูกนำมาใช้และกองกำลังนี้ข้อความที่จะส่งผ่านHtml::escape()จึงมีวิธีที่จะทำมันโดยไม่สมบูรณ์ผ่านการจัดรูปแบบการเชื่อมโยงของ Drupal
artfulrobot

คุณสามารถแทนที่บริการนี้เหมือนคนอื่น ๆ ได้ที่นี่tim.millwoodonline.co.uk/post/125163259445/…
rémy

0

ลองรหัสนี้:

{% 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 -%}

{% - endif%}

หรืออันนี้ (มาจาก: 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 %}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.