Drupal, Twig, Paragraphs, Felder - Feldinhalte verlinken und mit einem Linktarget belegen, flexibel dank twig

Drupal, Twig, Paragraphs, Felder - Feldinhalte verlinken und mit einem Linktarget belegen, flexibel dank twig

Twig ist seit Drupal 8 das Mittel der Wahl, wenn man die Ausgabe von Inhalten mit Templates gestalten möchte. Im Gegensatz zu den bisher verwendeten PHP-Templates ist die Syntax bei twig einfacher als bei PHP. Sinn und Zweck des Ganzen: Wer das Theme gestaltet, soll sich nur noch um die Ausgabe von Platzhaltern (= vordefinierten Variablen) und einfachen Kontrollstrukturen (if-Abfragen, for-Schleifen) kümmern müssen. Der Wegfall der teilweise unübersichtlichen php-Elemente erleichtert die Übersicht. Wir wollen das an einem einfachen Beispiel zeigen.

Die Aufgabenstellung

Eine Webseite besitzt einen Inhaltstyp "Report", der vor allem mit Paragraphs aufgebaut wird. Dazu haben wir verschiedene Paragraph-Typen (auch "Seitenabschnittstypen" in der deutschen Übersetzung von Drupal genannt) definiert. Ein solcher Paragraph-Type ist "Report-Textblock-Bild". Dieser enthält folgende Felder:

  • field_report_image: Ein Teaser-Image
  • field_report_title: Das Titelfeld
  • field_report_teaser: Ein kurzer Teaser-Text
  • field_report_link: Ein Link zu einem Artikel (kann auch ein externer Link sein.
  • filed_report_tag: Ein Taxonomy-Term

Alle Felder erlauben jeweils nur die Eingabe von einem Wert. Zusätzlich ist für das Link-Feld das Modul Link Attributes Widget installiert, das es gestattet für den Link z.B. ein Target ("_self" oder "_blank") zu setzen, das mit ausgegeben wird.

Vorbereitung: Das Debugging

Der übliche Weg wäre nun, einen Display Mode (Anzeigemodus) definieren, in dem man die auszugebenen Felder angibt und ggf. noch die eine oder andere Anzeigeoption setzt. So weit, so gut.  Nun möchten wir aber die Ausgabe so gestalten, dass die Felder report_image, report_title und report_teaser jeweils mit der URL von report_link verknüpft werden, während das Feld report_tag als Plain Text ausgegeben wird. Das lässt sich mit einem Display-Mode nicht realisieren. Aber mit einem eigenen twig-Template.

Dazu brauchen wir als erstes einen passenden Namen für die twig-Datei. Diesen liefert uns Drupal, indem wir die Debug-Funktion von twig aktivieren. Dazu tragen wir in ./sites/default/services.yml Folgendes ein:

parameters:
  twig.config:
    debug: true

Wenn wir jetzt die Seite aufrufen werden diverse zusätzliche Informationen im Quelltext ausgeben. Ein solcher Debug-Abschnitt sieht beispielsweise so aus:

<!-- FILE NAME SUGGESTIONS:
   * field--paragraph--field-content-class-item--content-row.html.twig
   * field--paragraph--field-content-class-item.html.twig
   x field--paragraph--content-row.html.twig
   * field--field-content-class-item.html.twig
   * field--entity-reference-revisions.html.twig
   * field.html.twig
-->
<!-- BEGIN OUTPUT from 'themes/custom/my_custom_theme/templates/field/field--paragraph--content-row.html.twig' -->
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'paragraph' -->
<!-- FILE NAME SUGGESTIONS:
   * paragraph--report-text-box-bild--default.html.twig
   * paragraph--report-text-box-bild.html.twig
   * paragraph--default.html.twig
   x paragraph.html.twig
-->

Hier interessieren uns die "File name suggestions" im letzten Abschnitt. Welche twig-Datei aktuell genommen wird, erkennt man an dem kleinen "x" am Anfang der Zeile. Je weiter unten die ausgewählte Datei steht, umso globaler ist ihre Bedeutung. In diesem Fall wird die Standard-Twig-Datei für Paragraphs genommen, die mit dem Theme geliefert wird. Diese wird für alle Paragraph-Types verwendet, die keine eigene twig-Datei besitzen. Wir wollen nun eine eigene Erstellen, die nur für den Paragraph-Type "Report-Textblock-Bild" gilt. Das wäre in diesem Fall die paragraph--report-text-box-bild.html.twig. Entsprechend kopieren wir die paragraph.html.twig aus dem Standard-Theme in unseren eigenen Theme-Ordner und benennen sie um in paragraph--report-text-box-bild.html.twig. Der Pfad lautet in unserem Beispiel

..\projekt\web\themes\custom\my_custom_theme\templates\paragraphs\paragraph--report-text-box-bild.html.twig

Wenn wir jetzt den Drupal-Cache leeren und unsere Seite mit F5 einmal refreshen, sieht der Abschnitt mit den file name suggestions so aus:

<!-- FILE NAME SUGGESTIONS:
   * paragraph--report-text-box-bild--default.html.twig
   x paragraph--report-text-box-bild.html.twig
   * paragraph--default.html.twig
   * paragraph.html.twig
-->

Daran erkennen wir, dass jetzt unsere eben erstellte twig-Datei verwendet wird. Da wir diese noch nicht weiter geändert haben, hat sich natürlich auch die Ausgabe unserer Seite nicht verändert.

Der Code

Um das gewünschte Ziel zu erreichen, öffnen wir unsere neue twig-Datei in unserem Lieblingseditor (Atome, Sublime, was auch immer ;-) ) und ersetzen im folgenden Abschnitt

{% block paragraph %}
  <div{{ attributes.addClass(classes) }}>
    <div class='underlay-wrapper'></div>
    <div class='overlay-wrapper'>
      {% block content %}
        {{ content }}
      {% endblock %}
    </div>
  </div>
{% endblock paragraph %}

die Zeile mit {{ content }} durch folgende Konstruktion:

{% if (content.field_report_link[0]['#url']|render|trim) %}
        <div class="field--name-field-report-image">
          <a href="{{ content.field_report_link[0]['#url']|render }}" target="{{ content.field_report_link[0]['#options'].attributes.target }}">
            {{ content.field_report_image }}
          </a>
        </div>
      {% else %}
        {{ content.field_report_image }}
      {% endif %}

        {{ content.field_tag }}

      {% if (content.field_report_link[0]['#url']|render|trim) %}
        <div class="field--name-field-report-title">
          <a href="{{ content.field_report_link[0]['#url']|render }}" target="{{ content.field_report_link[0]['#options'].attributes.target }}">
            {{ content.field_report_title }}
          </a>
        </div>
      {% else %}
        {{ content.field_report_title }}
      {% endif %}

      {% if (content.field_report_link[0]['#url']|render|trim) %}
        <div class="field--name-field-report-teaser">
          <a href="{{ content.field_report_link[0]['#url']|render }}" target="{{ content.field_report_link[0]['#options'].attributes.target }}">
            {{ content.field_report_teaser }}
          </a>
        </div>
      {% else %}
        {{ content.field_report_teaser }}
      {% endif %}

Und schon sind wir fertig. ;-)

Na gut, wir wollen noch erläutern, was die einzelnen Abschnitte machen.

{% if (content.field_teaser_link[0]['#url']|render|trim) %}

prüft, ob unser Link-Feld überhaupt einen Eintrag besitzt. Wenn es leer ist wollen wir auch keinen Link ausgeben. Der Eintrag ist übrigens gleichbedeutend mit

{% if (content.field_teaser_link[0]['#url']|render|trim) is not empty %}

Ist der URL-Eintrag also nicht leer so wird der folgende Abschnitt innerhalb der if-Abfrage ausgeführt:

        <div class="field--name-field-report-title">
          <a href="{{ content.field_report_link[0]['#url']|render }}" target="{{ content.field_report_link[0]['#options'].attributes.target }}">
            {{ content.field_report_title }}
          </a>
        </div>

Interessant sind hier der href- und der target-Teil. content.field_report_link[0]['#url']|render gibt uns die URL des Linkfeldes aus - eingebettet in ein <a>-Tag. Das target-Attribut erhalten wir mit

{{ content.field_report_link[0]['#options'].attributes.target

Man beachte hierbei die Art und Weise, wie in twig auf die verschiedenen Bestandteile des Feldes zugegriffen wird. Die Syntax ist zwar etwas einfacher, als bei PHPTemplate, aber nicht unbedingt einfach. Zugriffe auf "node.~" statt "content.~" oder "paragraph.~" haben jeweils eine etwas abweichende Notation. Hier empfiehlt es sich unbedingt die twig-Dokumentation zu Rate zu ziehen. Macht man hier nämlich einen Fehler, wird entweder nicht der gewünschte Inhalt ausgeben oder Drupal bricht die Ausgabe mit einer Fehlermeldung ab.

Ist der URL-Eintrag dagegen leer, so wird der else-Zweig der Abfrage ausgeführt. Mangels Link möchten wir jetzt nur den unverlinkten Title ausgeben und das geschieht hiermit:

      {% else %}
        {{ content.field_report_title }}
      {% endif %}

P.S.

Insgesamt kann man sagen, funktionieren Abfragen wie diese mit twig recht geschmeidig, wäre da nicht die krottenschlechte Doku. Bis man sich die Brocken zusammengesucht hat, geht gerne mal ein halber Arbeitstag drauf. Neben den Möglichkeiten und Regeln von twig  sind eben auch noch, wie oben kurz angerissen, die Besonderheiten von Drupal zu beachten. Die Drupal-Dokumentation bringt da leider nur sehr wenig Licht ins Dunkel und Google dokumentiert zuallererst, dass recht viele Entwickler mit demselben Problem zu kämpfen haben. Die Lösung findet sich dann irgendwann durch eine Mischung von Zufall und Genialität.