Šablonovací systém

Pro psaní šablon se používá šablonovací systém Twig, který je rozšířený o řadu funkcí, tagů, filtrů a proměnných. Tato rozšíření umožňují používat funkce a přistupovat k informacím o stránce uvnitř šablon.

Základní syntaxe

Twig používá tři typy závorek:

  • {{ ... }} – pro výpis hodnoty (echo)
  • {% ... %} – pro řídicí struktury (cykly, podmínky, atd.)
  • {# ... #} – pro komentáře

Proměnné

Proměnné šablon se vypisují na stránku pomocí dvojitých složených závorek.

{{ variable }}

Proměnné mohou také představovat výrazy.

{{ isAjax ? 'Ano' : 'Ne' }}

Proměnné lze spojovat pomocí znaku ~.

{{ 'Vaše jméno: ' ~ name }}

Výpis proměnných se automaticky escapuje. Pokud potřebujete vypsat HTML bez escapování, použijte filtr |raw:

{{ htmlObsah|raw }}

Jsou dostupné i globální proměnné pod proměnnou this:

→ Více o proměnných

Tagy

Tagy jsou unikátní funkcí Twig a jsou zabaleny do znaků {% %}.

{% tag %}

Tagy poskytují plynulejší způsob popisu logiky šablony.

{% if stormCloudComing %}
    Zůstaň uvnitř
{% else %}
    Jdi ven a hraj si
{% endif %}

Tag {% set %} lze použít k nastavení proměnných uvnitř šablony.

{% set activePage = 'blog' %}

Dostupné tagy

→ Více o tazích

Filtry

Filtry fungují jako modifikátory proměnných pro jedinou instanci a aplikují se pomocí pipe symbolu následovaného názvem filtru.

{{ 'string'|filter }}

Filtry mohou přijímat argumenty jako funkce.

{{ price|currency('CZK') }}

Filtry lze aplikovat postupně.

{{ 'October Glory'|upper|replace({'October': 'Morning'}) }}

Dostupné filtry

  • |app - vytváří URL odkaz na aplikaci
  • |page - vytváří URL odkaz na stránku
  • |theme - vytváří URL odkaz na asset v šabloně
  • |media - vytváří URL odkaz na soubor v media library
  • |resize - změní velikost obrázku
  • |default - poskytuje výchozí hodnotu
  • |raw - zobrazuje obsah bez HTML escapování
  • |md - zpracovává Markdown text
  • |_ - lokalizační filtr pro překlad textů
→ Více o filtrech

Cykly

Pro iteraci nad polem nebo kolekcí použijte smyčku for:

{% for produkt in produkty %}
  <p>{{ produkt.nazev }} - {{ produkt.cena }} Kč</p>
{% else %}
  <p>Žádné produkty k zobrazení.</p>
{% endfor %}

K dispozici je i objekt loop s informacemi o průběhu cyklu:

{% for i in 1..5 %}
  {{ loop.index }} / {{ loop.length }}
{% endfor %}

  • loop.index - Aktuální iterace smyčky. (od indexu 1)
  • loop.index0 - Aktuální iterace smyčky. (od indexu 0)
  • loop.revindex - Počet iterací od konce smyčky (od indexu 1)
  • loop.revindex0 - Počet iterací od konce smyčky (od indexu 0)
  • loop.first - True, pokud jde o první iteraci
  • loop.last - True, pokud jde o poslední iteraci
  • loop.length - Počet položek v sekvenci
  • loop.parent - Nadřazený kontext

Podmínky

Twig podporuje klasické if podmínky:

{% if user %}
  <p>Vítejte, {{ user.name }}!</p>
{% else %}
  <p>Vítejte, hoste!</p>
{% endif %}

Operátory

Twig podporuje běžné operátory:

  • Porovnávací: ==, !=, <, >, >=, <=
  • Logické: and, or, not
  • Slučovací: ?? pro výchozí hodnotu
{{ uzivatel.jmeno ?? 'Neznámý' }}

Testy

Twig obsahuje množství testů pro kontrolu typu a stavu:

{% if uzivatel is defined %}
{% if produkty is empty %}
{% if cislo is even %}

Dostupné testy zahrnují:

  • defined, empty, iterable, even, odd, same as, none

Funkce

Funkce umožňují spuštění logiky a návratový výsledek působí jako proměnná.

{{ function() }}

Funkce mohou přijímat argumenty.

{{ dump(variable) }}

Dostupné funkce

  • abort() - přeruší zpracování požadavku s HTTP chybou
  • redirect() - přesměruje uživatele na jinou URL
  • dump() - zobrazuje debug informace o proměnné
  • str() - poskytuje string helper funkce
  • form() - poskytuje helper funkce pro formuláře
  • html() - poskytuje helper funkce pro generování HTML
→ Více o funkcích

Logika přístupu

Nejdůležitější věc, kterou je třeba se naučit o Twig, je způsob, jakým přistupuje k PHP vrstvě. Pro zjednodušení {{ foo.bar }} provádí následující kontroly na PHP objektu:

  1. Zkontroluje, zda je foo pole a bar platný element.
  2. Pokud ne, a pokud je foo objekt, zkontroluje, zda je bar platná vlastnost.
  3. Pokud ne, a pokud je foo objekt, zkontroluje, zda je bar platná metoda (i když bar je konstruktor - použijte __construct() místo toho).
  4. Pokud ne, a pokud je foo objekt, zkontroluje, zda je getBar platná metoda.
  5. Pokud ne, a pokud je foo objekt, zkontroluje, zda je isBar platná metoda.
  6. Pokud ne, vrátí hodnotu null.

Příklady přístupu

{{ user.name }}           <!-- Vlastnost nebo metoda getName() -->
{{ user.isActive }}       <!-- Metoda isActive() -->
{{ user.getEmail() }}     <!-- Explicitní volání metody -->
{{ products[0] }}         <!-- Element pole na indexu 0 -->
{{ settings.app_name }}   <!-- Hodnota z konfigurace -->

Nepodporované funkce

Existují některé funkce z Twig, které nejsou podporovány. Jsou uvedeny níže vedle ekvivalentní funkce.

TagEkvivalent
{% extend %}Použijte Layouts nebo {% placeholder %}
{% include %}Použijte {% partial %} nebo {% content %}

V šablonovacím systému je vlastní systém pro správu layoutů a vkládání obsahu:

  • Layouts poskytují strukturovaný způsob definování layoutů stránek
  • Partials umožňují znovupoužitelné komponenty s parametry
  • Content blocks jsou určeny pro editovatelný obsah

Komponenty a částečné šablony (Partials)

Místo {% include %} se používají Partials.

Partials

Partials slouží pro vkládání opakujících se částí šablon (např. hlavička, patička):

{% partial 'site/header' %}

Partials mohou přijímat parametry:

{% partial 'blog/post' post=record %}

Komponenty

Komponenty se přidávají do stránky nebo layoutu a zobrazují se pomocí:

{% component 'blogPosts' %}

Praktické příklady

Základní stránka

{% put title %}{{ this.page.title }}{% endput %}

{% put head %}
    <meta name="description" content="{{ this.page.meta_description }}">
    {% if this.page.keywords %}
        <meta name="keywords" content="{{ this.page.keywords|join(', ') }}">
    {% endif %}
{% endput %}

{% put content %}
    <h1>{{ this.page.title }}</h1>
    
    {% if featuredImage %}
        <img src="{{ featuredImage|media|resize(800, 400) }}" 
             alt="{{ this.page.title }}">
    {% endif %}
    
    <div class="content">
        {{ content|raw }}
    </div>
    
    {% if this.param.category %}
        <nav class="breadcrumb">
            <a href="{{ 'blog'|page }}">{{ 'Blog'|_ }}</a>
            <span>/</span>
            <span>{{ this.param.category|title }}</span>
        </nav>
    {% endif %}
{% endput %}

Pokročilý layout

<!DOCTYPE html>
<html lang="cs">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
    <title>
        {% placeholder title default %}{{ this.theme.site_name }}{% endplaceholder %}
    </title>
    
    {% placeholder head %}
        <meta name="description" content="{{ this.theme.site_description }}">
    {% endplaceholder %}
    
    {% styles %}
        <link href="{{ 'assets/css/app.css'|theme }}" rel="stylesheet">
    {% endstyles %}
</head>
<body class="{{ this.page.id }}-page">
    <header>
        {% partial "header" %}
    </header>
    
    <main class="main-content">
        {% placeholder content %}
            <p>{{ 'Žádný obsah nebyl definován.'|_ }}</p>
        {% endplaceholder %}
    </main>
    
    <footer>
        {% content "footer.md" %}
    </footer>
    
    {% scripts %}
        <script src="{{ 'assets/js/app.js'|theme }}"></script>
    {% endscripts %}
    
    {% if this.environment == 'local' %}
        <!-- Debug panel pouze v dev prostředí -->
        {{ dump(this.page, this.param) }}
    {% endif %}
</body>
</html>

Komponenta s podmínkami

{% set showSidebar = this.page.id != 'home' and this.page.id != 'contact' %}

<div class="container {{ showSidebar ? 'with-sidebar' : 'full-width' }}">
    <div class="main-content">
        {% placeholder content %}
    </div>
    
    {% if showSidebar %}
        <aside class="sidebar">
            {% component "latestPosts" limit=5 %}
            {% partial "newsletter-signup" %}
        </aside>
    {% endif %}
</div>

Best Practices

1. Používejte správné tagy

<!-- ✅ Správně -->
{% partial "header" user=currentUser %}
{% content "about-us.md" %}
{% page "blog-post" slug=article.slug %}

<!-- ❌ Špatně -->
{% include "header.htm" %}  <!-- Nepodporováno -->

2. Escapování a bezpečnost

<!-- ✅ Bezpečné pro uživatelský obsah -->
{{ user.comment }}

<!-- ⚠️ Používejte pouze pro důvěryhodný obsah -->
{{ article.content|raw }}

<!-- ✅ Pro HTML z administrace -->
{{ settings.footer_html|raw }}

3. Výkon a optimalizace

<!-- ✅ Cache heavy operations -->
{% set expensiveData = getExpensiveData() %}
{% for item in expensiveData %}
    {{ item.name }}
{% endfor %}

<!-- ✅ Podmíněné načítání -->
{% if this.page.id == 'contact' %}
    {% component "contactForm" %}
{% endif %}