Show-Notes zu „Twig in der Praxis“
von Dennis Erdmann (Kommentare: 1)
In der letzten Contao Show für das Jahr 2024 sprachen Christian, Bernhard und ich über Twig in der Praxis.
Falls du die Show noch nicht gesehen hast, kannst du das hier nachholen: Die Contao (Morning) Show #20 - Twig in der Praxis. Die von mir gezeigten Beispiele kannst du dir hier noch einmal ansehen und nach Belieben kopieren und anpassen. Bernhards Beispiele für Rocksolid Custom Elements findest du hier: contaokurs.at
1. Überschriften um Klasse erweitern
So gut wie in jedem neuen Projekte, lege ich ein Twig Template an, um alle Überschriften der Inhaltselemente um eine Klasse headline
zu erweitern. Die Klasse hat den großen Vorteil, dass diese unabhängig von der Hierarchie gestalten kann und gleichzeitig weniger CSS benötige.
So sieht das Template aus:
{% use "@Contao/component/_headline.html.twig" %}
{% block headline_attributes %}
{% set headline_attributes = attrs()
.addClass('headline')
.mergeWith(headline.attributes|default)
%}
{{ headline_attributes }}
{% endblock %}
2. Akkordeon mit Container
Ein Problem, in das ich mit dem neuen Akkordeon-Element öfter mal reinlaufe, ist die Tatsache, dass Akkordeon-Toggler und -Inhalt nicht mehr von einem Container umschlossen werden. Dadurch ist es schwerer, diese beispielsweise mit einem Rahmen zu gestalten.
Aber mit der folgenden Template-Anpassung erzeuge ich zusätzlichen Container mit der Klasse accordion-container
:
{% extends "@Contao/content_element/accordion.html.twig" %}
{% block element %}
<div class="accordion-container">
{{ parent() }}
</div>
{% endblock %}
3. Akkordeon mit JSON-LD für FAQ
In einem Projekt, an dem ich aktuell arbeite, wollten wir je nach Bedarf FAQ als Akkordeon darstellen. Diese sollten sich möglichst variabel einsetzen lassen, sodass wir uns gegen das FAQ-Modul entschieden, dass Contao standardmäßig mitbringt.
Stattdessen erweiterten wir das Akkordeon-Element und fügten eine Checkbox hinzu, um bei Bedarf die Inhalte durch JSON-LD für FAQ zu ergänzen. In der Contao Show zeigte ich eine alternative Lösung, bei der man ein Varianten-Template accordion/faq.html.twig im Akkordeon-Element auswählt.
Damit die Daten aus dem Akkordeon als Frage und Antwort für das FAQ-Schema dienen können, habe ich außerdem eine Twig Funktion geschrieben, die diese Daten verarbeitet und an das Template zurückgibt.
Hier die Twig Function:
// src/Twig/AccordionExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AccordionExtension extends AbstractExtension
{
public function getFilters(): array
{
return [
new TwigFilter('jsonld_accordion', [$this, 'generateJsonLdAccordion']),
];
}
public function generateJsonLdAccordion(array $elements): array
{
$jsonLd = [
"@context" => "https://schema.org",
"@type" => "FAQPage",
"mainEntity" => []
];
foreach ($elements as $element) {
// Remove HTML tags and line breaks
$cleanedContent = strip_tags($element['content']);
$cleanedContent = str_replace(["\n", "\r"], ' ', $cleanedContent);
$jsonLd['mainEntity'][] = [
"@type" => "Question",
"name" => $element['header'],
"acceptedAnswer" => [
"@type" => "Answer",
"text" => $cleanedContent
]
];
}
return $jsonLd;
}
}
und hier das Template:
{% extends "@Contao/content_element/accordion.html.twig" %}
{% set accordionItems = [] %}
{% for element in elements %}
{% set accordionItems = accordionItems|merge([{
'header': element.header,
'content': content_element(element.reference)
}]) %}
{% endfor %}
{% block script %}
{{ parent() }}
{% do add_schema_org(jsonld_accordion(accordionItems)) %}
{% endblock %}
4. Navigation erweitern mit Twig
Auch wenn es für die meisten Frontend-Module keine Twig-Templates gibt, lässt sich Twig für diese verwenden. Das ist in meisten Fällen nur Mehraufwand. Aber ein Beispiel ist mir dennoch eingefallen, bei dem die Umsetzung mit Twig deutlich einfacher ist, als eine Umsetzung mit HTML5-Templates.
In Projekten kommt es immer wieder vor, dass ich Navigationspunkte in Menüs durch Icons oder Bilder ergänzen soll. Damit diese auch für Redakteure pflegbar sind, erweitere ich im ersten Schritt die Seitenstruktur per DCA:
// contao/dca/tl_page.php
use Contao\CoreBundle\DataContainer\PaletteManipulator;
use Contao\Config;
PaletteManipulator::create()
->addLegend('image_legend', 'meta_legend', PaletteManipulator::POSITION_BEFORE)
->addField('navigation_image', 'image_legend', PaletteManipulator::POSITION_APPEND)
->applyToPalette('default', 'tl_page')
->applyToPalette('regular', 'tl_page')
->applyToPalette('redirect', 'tl_page')
;
$GLOBALS['TL_DCA']['tl_page']['fields']['navigation_image'] = [
'label' => &$GLOBALS['TL_LANG']['tl_page']['navigation_image'],
'exclude' => true,
'search' => true,
'flag' => 1,
'inputType' => 'fileTree',
'eval' => [
'fieldType' => 'radio',
'files' => true,
'filesOnly' => true,
'extensions' => Config::get('validImageTypes')
],
'sql' => "blob NULL",
];
Dadurch wird ein Bildauswahl-Feld erzeugt und auf dieses Bild kann ich dann in meinem Template zugreifen. Mit den auf PHP basierenden HTML5-Templates müsste ich, damit ich es abrufen, umwandeln und ausgeben kann, auf mehrere Contao Klassen zugreifen und diese in meinem Template laden.
Dank Twig geht das aber deutlich einfacher:
{% if item.navigation_image is not empty %}
{{ contao_figure(item.navigation_image, [20, 20, proportional]) }}
{% endif %}
Erklärung: Wenn ein navigation_image vorhanden ist, dann soll das Bild per {{contao_figure(IMAGE_PATH, IMAGE_SIZE)}}
ausgegeben werden.
So sieht das komplette Template aus:
<ul class="{{ level }}">
{% for item in items %}
{% if item.isActive %}
<li class="{{ item.class }}">
<strong class="{{ item.class }}" {% if item.subitems is not empty %} aria-haspopup="true"{% endif %}>
{% if item.navigation_image is not empty %}
{{ contao_figure(item.navigation_image, [20, 20, proportional]) }}
{% endif %}
{{ item.link }}
</strong>
{{ item.subitems|default|raw }}
</li>
{% else %}
<li{% if item.class %} class="{{ item.class }}"{% endif %}>
<a href="{{ item.href }}" title="{{ item.pageTitle ?: item.title }}" {% if item.class %} class="{{ item.class }}"{% endif %} {% if item.accesskey is not empty %} accesskey="{{ item.accesskey }}"{% endif %} {{ item.target }} {{ item.rel|default('') }} {% if item.subitems is not empty %} aria-haspopup="true"{% endif %}>
{% if item.navigation_image is not empty %}
{{ contao_figure(item.navigation_image, [20, 20, proportional]) }}
{% endif %}
{{ item.link }}
</a>
{{ item.subitems|default|raw }}
</li>
{% endif %}
{% endfor %}
</ul>
5. fe_page mit Twig erweitern
Auch die fe_page
lässt sich per Twig nutzen und die Blöcke, die im HTML5-Template definiert wurden, lassen sich überschreiben oder erweitern. In einer normalen Projekt, sehe ich da zwar momentan keinen Vorteil, da sich keine Zeilen sparen ließen, aber für Entwickler:innen von Erweiterungen könnte das durchaus interessant sein.
Hier mein Beispiel, bei dem ich einen eigenen Layout-Bereich Navigation im Header nach dem .inside
-Container einfüge:
{% extends '@Contao/fe_page' %}
{% block header %}
{% if header %}
<header id="header">
<div class="inside">
{{ header|raw }}
</div>
{{ contao_section('navigation') }}
</header>
{% endif %}
{% endblock %}
Die Anregung von Fritz, Template-Anpassungen in der Conto Doku zu sammeln, begrüße ich nach wie vor. Allerdings fehlt mir gerade die Zeit, mich in die Doku (und wie man sie ergänzt) einzuarbeiten.
Aber wenn du dich mit der Doku auskennst und Zeit und Lust hast, diese mit um die Beispiele zu ergänzen, gerne!
Kommentare
Kommentar von Martin |
Guter Ansatz! Eine Contao Twig Doku wäre top!
Einen Kommentar schreiben