Přejít k navigační liště

Zdroják » PHP » Symfony po krůčkách – Twig

Symfony po krůčkách – Twig

Články PHP

V dnešním díle si představíme výchozí symfoňácký šablonovací systém – Twig, který nám umožňuje krásně oddělit aplikační vrstvu od prezentační. Díky šablonám nemusíte (a ve Twigu ani nemůžete) míchat PHP a HTML kód. Další výhodou Twigu je bezesporu zvýšení bezpečnosti vaší aplikace za pomocí automatického escapování obsahu proměnných.

Základní syntaxe Twigu

Potřebujeme znát 3 základní syntaktické konstrukce:

  • {{ … }} něco říká, používá se např. pro vykreslení proměnné.
  • {% … %} něco dělá a používá se v případě, kdy v šabloně potřebujeme nějakou logiku, například definovat proměnnou, zapsat podmínku nebo třeba importovat jinou šablonu.
  • {# … #} něco komentuje.

Příklad na úvod

Všechny zdrojáky najdete v repozitáři na GitHubu.

Twig nainstalujeme pomocí Composeru

composer require twig/twig

Vytvoříme jednoduchý skript, který bude vykreslovat šablonu Resources/views/index.html.twig. Vystačíme si se dvěma třídami:

  • Twig_Loader_Filesystem – třída zajišťující načítání šablon. V konstruktoru jednoduše nastavíme cestu ke složce, ve které má šablony hledat.
  • Twig_Environment – slouží ke konfiguraci našeho Twig prostředí. Jako první parametr přebírá implementaci Twig_LoaderInterface, která se bude používat pro načítání šablon.  V našem případě je to právě Twig_Loader_Filesystem.
//index.php
require_once __DIR__ . '/vendor/autoload.php';

$loader = new Twig_Loader_Filesystem('Resources/views');
$twig = new Twig_Environment($loader);

echo $twig->render('index.html.twig', ['name' => 'world']);
// Hello world!

Metodou $twig->render() vykreslíme šablonu, kterou definujeme jako první parametr. Druhým parametrem si do šablony můžeme předat pole proměnných. Naše základní šablona pak vypadá takto:

{# index.html.twig #}
Hello {{ name }}!

Přístup k atributům objektu a prvkům pole

Do šablony si můžeme předat i pole nebo objekt, k jehož prvkům (resp. properties) pak můžeme přistupovat unifikovaným způsobem pomocí tečky:

{# index.html.twig #}
My favourite movie: {{ movie1.title }}<br>
My friends favourite movie: {{ movie2.title }}
//index.php
require_once __DIR__ . '/vendor/autoload.php';

class Movie {

 /**
  * @var string
  */
 private $title;

 /**
  * @param string $title
  */
 public function __construct($title) {
  $this->title = $title;
 }

 /**
  * @return string
  */
 public function getTitle() {
  return $this->title;
 }
}

$loader = new Twig_Loader_Filesystem('Resources/views');
$twig = new Twig_Environment($loader);

$movieAsObject = new Movie('Kill Bill');
$movieAsArray = ['title' => 'Matrix']; 

echo $twig->render('index.html.twig', [
 'movie1' => $movieAsObject, 
 'movie2' => $movieAsArray, 
]);
// My favourite movie: Kill Bill
// My friends favourite movie: Matrix

Cykly, podmínky, operátory

Ve Twigu můžeme používat i cykly a podmínky, k dispozici máme navíc i rozsáhlou sadu operátorů, díky kterým je práce se šablonami zase o něco jednodušší. Operátory můžeme rozdělit na

  • logické (např. and, or)
  • porovnávací/testovací (např. ==, is not, empty nebo matches, pokud chceme používat regulární výrazy)
  • a další (např. ~ pro konkatenaci stringů, ternární operátor ?: nebo samozřejmě klasické matematické operátory pro sčítání, odčítání atd.).

Abychom nezůstávali dlouho u teorie, ukažme si jednoduchý příklad použití v kódu:

//index.php
require_once __DIR__ . '/vendor/autoload.php';
class Movie {

 /**
  * @var string
  */
 private $title;

 /**
  * @var string|null
  */
 private $comment;

 /**
  * @param string $title
  * @param string|null $comment
  */
 public function __construct($title, $comment = null) {
  $this->title = $title;
  $this->comment = $comment;
 }

 /**
  * @return string
  */
 public function getTitle() {
  return $this->title;
 }

 /**
  * @return string|null
  */
 public function getComment() {
  return $this->comment;
 }
}

$loader = new Twig_Loader_Filesystem('Resources/views');
$twig = new Twig_Environment($loader);

$movies = [
 new Movie('Reservoir Dogs'),
 new Movie('Jackie Brown'),
 new Movie('Grindhouse'),
 new Movie('Pulp Fiction', 'Oscar for Best Writing'),
 new Movie('Kill Bill'),
];

echo $twig->render('index.html.twig', [
 'movies' => $movies,
]);
{# index.html.twig #}
Quentin Tarantino movies: <br>
<ul>
 {% for movie in movies %}
  <li>
     {{ movie.title }}
     {% if movie.comment is not null %}
        - <i>{{ movie.comment }}</i>
     {% endif %}
  </li>
 {% endfor %}
</ul>

Co ještě Twig umí?

 Filtry a funkce

Filtry nám slouží nejčastěji k formátování zobrazovaných dat. Užitečné filtry jsou např. length, za jehož pomoci zjišťujeme počet prvků v poli, date pro formátování data, number_format pro formátování čísel nebo trim pro odřezání bílých znaků. Filtr aplikujeme jednoduše pomocí svislítka.

{{ movies|length }}

Funkce většinou provádějí nějakou pokročilejší logiku. Jako příklad funkce může posloužit třeba constant() pro vypisování hodnot PHP konstant. Funkci pak voláme přesně tak, jak by programátor očekával:

{{ constant('Component\\Constraint::MAX_VALUE') }}

Další užitečnou funkcí může být date() pro získání aktuálního data, která funguje obdobně jako v PHP.

{{ date(-2days) }}

Seznam všech funkcí a filtrů najdeme v oficiální dokumentaci.

Filtry a funkce si navíc můžeme vytvářet i vlastní, což si popíšeme v příštím díle.

Dědičnost šablon

Další fantastickou vlastností Twigu je možnost tvořit hierarchii šablon pomocí dědičnosti. Díky tomu lze například vytvořit šablonu, která bude sloužit jako kostra pro základní layout, a tu budou ostatní šablony rozšiřovat pomocí přepisování konkrétních bloků. Dědičnost se definuje pomocí tagu extends.

{# base.html.twig #}
<html>
    <head>
        <meta charset="UTF-8">
        <title>Twig - {% block title %}{% endblock %}</title>
    </head>
    <body>
        <div id="menu">
            This is menu
        </div>
        <div id="main_content">
            {% block main_content %}{% endblock %}
        </div>
    </body>
</html>
{# index.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}
    Index page
{% endblock %}

{% block main_content %}
   This is main content
{% endblock %}

Ve výsledku pak uživatel na stránce uvidí toto:

This is menu
This is main content

Escapování

Jak už bylo zmíněno v úvodu, automatické escapování obsahu proměnných je jednou z klíčových vlastností šablonovacích systémů, Twig nevyjímaje. Pokud totiž v šabloně vykresluji proměnnou, je zde vždy riziko, že bude obsahovat znaky, které mohou výsledné HTML rozbít. Navíc pokud na escapování zapomenu, jsem vystaven riziku napadení své aplikace XSS útokem.

Ve Twigu je escapování defaultně zapnuto pro HTML, takže se o něj jako programátor nemusím vůbec starat. Jsou ovšem případy, kdy potřebuji escapování zakázat, např. pokud mám v proměnné naformátovaný obsah článku. V takovém případě můžu použít filtr

{{ article.text|raw }}

Mám taky možnost v konfiguraci escapování zakázat úplně a postarat se o něj ručně, a to hned několika možnými způsoby:

  • Pomocí filtru
{{ user.username|escape }}
  • nebo zkráceně
{{ user.username|e }}
  • nebo pomocí tagu
{% autoescape %}
  {{ user.username }}
{% endautoescape %}

Twig také umožňuje změnit strategii escapování (css, js, url, html_attr), což  lze opět provést buď v konfiguraci

new Twig_Environment($loader, ['autoescape' => 'url']);

nebo při používání filtru

{{ urlAddress|e('url') }}

případně při používání tagu

{% autoescape 'url' %}
   {{ urlAddress }}
{% endautoescape %}

Zase o krok dále

Dnes jsme se dozvěděli,

  • že díky používání Twigu máme zajištěno automatické escapování a přehlednější zdrojáky
  • že Twig umí spoustu užitečných věcí, jako je třeba dědičnost šablon
  • že použití Twigu v naší aplikaci je opravdu hračka

Co nás čeká příště?

V dalším díle se na Twig podíváme ještě podrobněji. Na co se můžete těšit?

  • Twig Extensions a integrace Twigu v Symfony,
  • používání překladů v šablonách,
  • tvorba vlastní funkce a filtru,
  • makra,
  • maily a další možnosti využití šablon

Komentáře

Subscribe
Upozornit na
guest
33 Komentářů
Nejstarší
Nejnovější Most Voted
Inline Feedbacks
View all comments
Oldis

hm nic moc, teda clanek asi dobrej, ale syntaxe twigu, to by se jeden upsal psat vsude end neco.

Karlos

Pokud píšete v PSPadu tak asi ano, možná by jste se upsal :)

Jarda

Díky šablonám nemusíte (a ve Twigu ani nemůžete) míchat PHP a HTML kód.

Proč je to výhoda? Přijde mi, že jste jenom nahradili jeden programovací jazyk jiným. Místo abyste míchali PHP a HTML mícháte HTML a Twig.

hatto

zopar vyhod:

  1. templaty su ovela prehladnejsie nakolko syntax je adaptovana pre html
  2. vela veci co treba riesit v php na par riadkov su v twigu nativne
  3. spominana dedicnost je asi najvacsia vyhoda
  4. include inych templatov
  5. nativne filtre a funkcie
    atd atd

twig pouzivame nie len v symfony projektoch, ale takisto pre staticke webstranky ci newslettre (pomocou php compilacie alebo gulp-twig). v podstate okrem drupal7 php na front-ent nepouzivame.

wordpress projekty bez twig (plugin timber) uz nerobim, oddeli sa ‚fuknkcna‘ cast kodu od templatu a do twig templatu uz posielam pripravene premenne a tak mam nieco v zmysle ‚VC‘

aj drupal 8 uz konecne podporuje twig.

odporucam vyskusat a urobit si vlastny nazor, ale zatial nepoznam nikoho, kto by radsej miesal ‚php a html‘ :)

Lukáš Brzák

Výhoda je, že všichni krom programátora se v šabloně krásně vyznají a ví, že cokoli v blocích {% %}, {{ }} nemají upravovat ;-)

Lukáš Brzák

Jednoduše proto, že míchání kódu do šablon smrdí několika problémy. Nejdůležitější je čitelnost šablon pro kodéry, kteří cokoli v {% %}, {{ }} a {# #} jednoduše ignorují.

Lukáš Brzák

Chápu, že je článek zaměřen pro začátečníky. Nicméně je třeba dodat, že si můžeme Twig opravdu jednoduše rozšiřovat o jakékoli vlastní funkce. Vím, že spousta lidí toto potřebuje, je zvyklá na vlastní funkce (např. ve Smarty apod.) Viz dokumentace.

Výhodou Twigu zabudovaného uvnitř Symfony je, že nemusíte nic nastavovat a z Controlleru rovnou voláte metodu render() takto:

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class MyController extends Controller
{
    public function helloWorldAction()
    {
        return $this->render('layout.html.twig', [
            'cosi' => 'hodnota',
        ]);
    }
}

Případně můžeme metodu Controlleru nastavovat pomocí anotací:

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class MyController extends Controller
{
    /**
     * @Template("AppBundle:Frontend:layout.html.twig")
     */
    public function showAction($id)
    {
        return [
            'cosi' => 'hodnota'
        ];
    }
}

Pro více informací o anotacích v Symfony najdete v dokumentaci…

Lukáš Brzák

Asi bych měl články číst do konce :-)))))

zajca

Pěkný článek, někdy mě taky štve ta ukecanost twigu, ale přijde mě to lepší než cpat značky do html jak to dělá třeba nette (nevím jak se jmenuje ten systém tak říkám nette).
Každopádě přístě doufám bude i embed, řekl bych, že se na něj dost zapomíná.
A trocha hnidopištví: rozhodně bych se nebál napsat, že date() je prostě DateTime https://github.com/twigphp/Twig/blob/1.x/lib/Twig/Extension/Core.php#L486

Taco

Jmenuje se to latte. A zajímalo by mě v čem vidíte, že je zápis twigu lepší? Zatím vidím zásadnější výhodu twigu v tom, že neumožňuje eval. Jinak umí cca to samé co latte. A zdá se, že i kontextové escapování (což byla až do nedávna unikátní feature pouze latte).

Taco

No Twig nemá automatické kontextové escapování, ale upřímně, jak často to člověk využije?

Ehm, vždycky?! Zvláště pokud děláte v html, a čtete Špačka?

Miroslav Šustek

Twig podporuje nastavení 'autoescape' => 'filename' (namísto standardního 'html') – viz http://twig.sensiolabs.org/doc/api.html#environment-options.

Pak autoescapuje v každém souboru podle jeho přípony. Takže template.html.twig escapuje jako HTML, template.css.twig jako CSS atd.

Pokud nepotřebujete míchat více jazyků v jedné šabloně, tak se bez kontextového escapování, jako je v Latte, obejdete.

Taco

Ach jo, to jsou pořád argumenty…

Ve výsledku se obejdu bez spousty věcí, vždycky to můžu psá v čistém php.

takyhonza

:D :D :D :D tohle pobavilo :D :D :D :D

Ne, teď vážně. Pokud šablonovací systém neumí automatické kontextové escapování a kontexty tudíž musím escapovat ručně, tak je mi takový šablonovací systém popravdě řečeno úplně k ničemu. To můžu psát šablony rovnou v čistém PHP (a naučit kodéry, že na kód mezi <?php a ?> se nesahá ;)) a nepotřebuji k tomu instalovat a nastavovat zbytečně další knihovnu :)

Sleduju tuhle sérii článků pravidelně a přestože si nemyslím, že by Symfony balíčky byly špatné, z toho co jsem viděl mi příjde, že místo praktičnosti z těch knihoven čiší nějaká překomplikovaná akademická čistota, či co. V případě Twigu je to vidět už jen na tom, že existují 2 syntaxe maker – jedna „vypisovací“ a druhá „akční“. Beru tyhle články jako zajímavé okénko, ale používat něco z představených balíků denně, to bych fakt nechtěl :)

petrsoukup

Za ty roky s Twigem jsem ještě nenarazil na situaci, kde bych potřeboval automatické escapování.
V drtivé většině si vystačím s defaultním html režimem (tzn vůbec to neřeším) a když náhodou potřebuju něco speciálního jako třeba atribut, tak to prostě výslovně uvedu (<a href="{{url|e('html_attr')}}">). Na druhou stranu mě ale nenapadá kód, který bych do atributu nemohl escapovat defaulním html režimem a způsobil by nějaký zásadní problém.

Samozřejmě by bylo super, kdyby twig kontextové escapování měl. Je to ale důsledek toho, jak vůbec funguje. Neparsuje šablonu, takže kontext nezná. Zároveň ale díky tomu jde snadno použít na cokoliv a nejen HTML.

Nad Twigem nicméně existuje ještě nadstavba Twital (https://github.com/goetas/twital). Tam už se šablona parsuje (a musí tedy být HTML5 validní), takže tam by kontextové escapování fungovalo.

takyhonza

Jasně, výchozí HTML escapování dozajista stačí v celé řadě případů, ale běžně přecházím z jednoho kontextu do druhého tak často, že nechci myslet na to, že musím ručně přepnout kontext, aby se data vyescapovala správně. Pokud používám šablonovací engine, potřebuji a chci se spolehnout na to, že escapování vyřeší v 99% případů bez mého zásahu.

I Latte se dá použít na cokoli a nejen HTML :)

Nette fanboy

Fascinuje mě vyzdvihování kontextového escapování v latte a přitom nikdo nezmíní super featuru:

{php echo $_SERVER['HTTP_HOST'];}
<?php echo $_SERVER['HTTP_HOST']; ?>
Taco

Zmínil. Jen je otázka, zda je to výhoda nebo nevýhoda latte – skutečnost, že toto nejde moc dobře zakázat (a nebo minimálně nevím jak).

Pavel

Je to Latte a do html tagů nic spát nemusíš. Můžeš používat klasické makra místo n:makra. Někomu vyhovuje jedno a někomu to druhé.

it expert

Neco mi to silne pripomina, ze by Jinja2 ?

petrsoukup

Dokonce to má Twig napsané na hlavní straně – vychází ze syntaxe Jinja2

Ivan

Este by som doplnil ze pisat tagy s pomlckou vas zbavia novych riadkov {{- nieco -}}, {%- if nieco -%}…

podhy

nejen nových řádků, ale všech bílých znaků

podhy

Trochu mi tady chybí zmínka o velice silné vlastnosti, kterou je sandbox což např zde zmiňované Latte nemá (teda nemělo co jsem si o ně naposledy četl).

Adam

Zdravim,
TWIG jsme pouzivali cca 2 roky na jednom vetsim projektu, ale po nejake dobe nas zacla brzdit doba kompilace sablon a hlavne nemoznost pouzivat sablony na strane klienta. Presli jsme tedy na React a jsme moc spokojeni. Produktivita je o nekolik levelu vyse a sablony muzeme pouzivat jak na strane serveru tak na strane klienta.

podhy

Co se týče kompilace tak tam těžko něco poradit (já na toto ještě nenarazil), ale šablony se na straně klienta používat dají viz https://github.com/justjohn/twig.js

Adam

S twig.js mate pravdu, nicmene tato knihovna nepokryva veskerou funkcionalitu serverove verze, tudiz je temer nepouzitelna, to uz ani nezminuji tvorbu vlastnich rozsireni. Pokud bych chtel vytvorit TWIG rozsireni, musel bych jej vytvorit 2x, jednou pro PHP TWIG a jednou pro twig.js, coz mi neprijde zrovna efektivni. Pokud namisto TWIGu pouziji treba React.js, tak takovehle nesmyslnosti resit vubec nemusim, mam jednu knihovnu jak pro server side, tak pro client side.

podhy

to je pravda. s tím musím souhlasit. nicméně twig.js už pokrývá značnou část twigu a je to aktuálně jen otázka času viz https://github.com/justjohn/twig.js/wiki/Implementation-Notes (navíc ten projekt je poměrně živej). Kolik reálně vlastních rozšíření do twigu potřebujete? Já v současné době mám jen dvě a i tak bych se bez nich obešel.
U reactu člověk zase musí pořešit server rendering (teda já osobně bych ho určitě musel řešit) a to ve spojení s PHP taky není úplně nejtriviálnější.

zvedavec

ako presne renderujes React na serveri?
cez node.js alebo existuje dake napojenie na symfony? nieco ako nahrada napr twigu?

Enum a statická analýza kódu

Mám jednu univerzální radu pro začínající programátorty. V učení sice neexistují rychlé zkratky, ovšem tuhle radu můžete snadno začít používat a zrychlit tak tempo učení. Tou tajemnou ingrediencí je statická analýza kódu. Ukážeme si to na příkladu enum.