Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Twig-Vererbung und Layouts

Twig-Vererbung und Layouts

~13 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Ohne Vererbung müsste JEDES Template Navigation, Footer und HTML-Grundgerüst wiederholen. Twigs Vererbungssystem löst das elegant – GENAU das Muster, das wir in Kapitel 12 für Flash Messages bereits vorweggenommen haben.

Das Basis-Template: base.html.twig

templates/base.html.twig
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Aufgaben-Manager{% endblock %}</title>
    {% block stylesheets %}{% endblock %}
</head>
<body>
    <nav>
        <a href="{{ path('project_index') }}">Projekte</a>
    </nav>

    {% for label, messages in app.flashes %}
        {% for message in messages %}
            <div class="alert alert-{{ label }}">{{ message }}</div>
        {% endfor %}
    {% endfor %}

    <main>
        {% block body %}{% endblock %}
    </main>

    {% block javascripts %}{% endblock %}
</body>
</html>

{% block name %}...{% endblock %} definiert eine BENANNTE Stelle, die kindliche Templates ÜBERSCHREIBEN können. path('project_index') löst einen Routennamen (Kapitel 7) zur tatsächlichen URL auf – GENAU wie redirectToRoute() im Controller, niemals eine URL hartcodieren.

Ein kindliches Template: extends

templates/project/index.html.twig
{% extends 'base.html.twig' %}

{% block title %}Projekte – {{ parent() }}{% endblock %}

{% block body %}
    <h1>Projekte</h1>

    <ul>
        {% for projekt in projekte %}
            <li>{{ projekt.name }}</li>
        {% endfor %}
    </ul>
{% endblock %}

{% extends %} MUSS die erste Zeile des Templates sein. {{ parent() }} innerhalb eines Blocks fügt den INHALT des übergeordneten Blocks ein – hier: Projekte – Aufgaben-Manager statt die Basis-Beschriftung komplett zu ersetzen.

Wiederverwendbare Teil-Templates: include

Für Bausteine, die auf MEHREREN Seiten unabhängig von der Vererbungshierarchie vorkommen (z. B. eine Projekt-Karte), eignet sich {% include %} besser als Vererbung:

templates/project/_karte.html.twig
<div class="projekt-karte">
    <h3>{{ projekt.name }}</h3>
    <a href="{{ path('project_show', {id: projekt.id}) }}">Details</a>
</div>
{% for projekt in projekte %}
    {{ include('project/_karte.html.twig', {projekt: projekt}) }}
{% endfor %}

Der führende Unterstrich (_karte.html.twig) ist reine Konvention (kein Symfony-Zwang), signalisiert aber "dieses Template wird nur EINGEBUNDEN, nicht direkt aus einem Controller gerendert".

path('project_show', {id: projekt.id}) zeigt, wie Route-Parameter (Kapitel 9) aus Twig heraus übergeben werden – als zweites Argument, ein Objekt-Literal mit den Platzhalter-Namen als Schlüssel.

Vererbung vs. include: eine Faustregel

MechanismusEinsatzfall
extendsFür die GESAMTE Seitenstruktur – EINE Basis, viele Seiten füllen ihre Blöcke.
includeFür WIEDERVERWENDBARE Bausteine INNERHALB einer Seite, unabhängig von der Block-Hierarchie – z. B. eine Karte, ein Formular-Fragment.

CSS und JavaScript einbinden: die asset()-Funktion

{% block stylesheets %}
    <link rel="stylesheet" href="{{ asset('styles/app.css') }}">
{% endblock %}

Tipp: asset('styles/app.css') löst den Pfad relativ zu public/ auf und berücksichtigt automatisch ein konfiguriertes "Asset-Versioning" (Cache-Busting per Query-String) – robuster als einen hartcodierten /styles/app.css-Pfad.