Twig – Dobry system szablonów

Twig – Dobry system szablonów

Systemy szablonów (w przypadku aplikacji webowych) są to zazwyczaj dodatkowe biblioteki odpowiedzialne za przetworzenie danych wejściowych i na ich podstawie wygenerowanie kompletnego kodu html. Nadrzędny cel jest tutaj jeden: oddzielenie logiki biznesowej od warstwy prezentacji aplikacji.

Powinieneś wiedzieć, że język php wykorzystywany do tworzenia stron internetowych również może być wykorzystany jako język szablonów. Często jest to uzasadnione. Nie musisz się uczyć i wdrażać w żadną dodatkową technologię, w żaden sposób nie ogranicza Cię składnia wybranej technologii i możesz się spodziewać maksymalnej wydajności tego rozwiązania – w końcu nie dołączasz do projektu dodatkowych zależności. Czy jest to jednak optymalne rozwiązanie?

Otóż nie. Jeśli tworzysz jakiś projekt rzadko kiedy jest uzasadnione pisanie wszystkiego od zera. Często sięgasz po framework, który może znacznie zwiększyć efektywność Twojej pracy, ale również dostarczyć dobre, sprawdzone i przemyślane rozwiązania. Kluczowe jest tutaj wybranie technologii, która faktycznie umożliwi Ci przygotowanie lepszego (bierzemy pod uwagę również jakość i koszt utrzymania kodu źródłowego) oprogramowania. Wokół sensu korzystania z systemów szablonów toczy się wiele dyskusji. Moim zdaniem bezcelowych: nie ma jednej ścieżki do najefektywniejszego przeprowadzenia wszystkich projektów. Różne projekty mają odmienne wymagania w stosunku do czasu realizacji, wydajności, bezpieczeństwa czy też zależności.

Wierzę, że Twig, któremu poświęciłem tą notkę pozwoli Ci przygotowywać lepsze aplikacje webowe. Jeśli dotychczas nie miałeś do czynienia z żadnymi systemami szablonów, czy też masz złe wspomnienia związane z przestarzałymi technologiami (np. Smarty) ten artykuł jest dla Ciebie.

Twig – zalety oraz wady

Moim zdaniem warto korzystać z Twig-a. Dołączenie go do projektu pozwoli Ci w elegancki sposób odseparować logikę od wyglądu aplikacji. Koniec z mieszaniem tych elementów nawet na „potrzebę chwili”. Efekt uboczny? Znacznie większe bezpieczeństwo Twojej aplikacji. Twig zapewnia dostęp do z góry określonych metod oraz funkcji. Zazwyczaj nie będzie Cię to w żaden sposób ograniczało, a pozwoli uniknąć sytuacji, w której ktoś w szablonie dodał elementy szkodliwe dla aplikacji. Pamiętaj: Twig to język szablonów, a nie programowania.

Bardzo istotna jest składnia tego systemu szablonów. Jest możliwie zwięzła, intuicyjna oraz prosta do nauki. Bez problemu wdrożą się w nią osoby nie mające dużego doświadczenia z programowaniem. Tworzenie szablonów bez problemu będzie mogło być wydelegowane dla osób mających głównie do czynienia z frontendem aplikacji internetowych, bez solidnego ich wdrażania w sam backend. Nawet jeśli odpowiadasz za całą aplikację pozwoli Ci to ją lepiej ustandaryzować i łatwiej Ci będzie nad nią zapanować w przyszłości. Jak się za jakiś czas przekonasz składnia Twig-a dużo lepiej nadaje się do zapisu szablonów, aniżeli użycie samego języka programowania jakim jest php.

Należy wspomnieć o największej zalecie Twig-a: rozszerzalności. Programista bez problemu będzie mógł wzbogacić język szablonów o dodatkowe metody, filtry, a nawet i tagi. Nie masz tutaj ograniczonej listy funkcji, na które będziesz skazany. Możesz skorzystać z już istniejących rozszerzeń, przygotować własne oraz w pełni zintegrować Twig-a z Twoją aplikacją/cms-em.

Co z wydajnością? Twig to szybkie rozwiązanie. Raz skompilowane szablony mogą być zapisywane do cache. Do tego jest możliwość zainstalowania na serwerze natywnego modułu, który jeszcze bardziej przyspieszy kompilowanie. Oczywiście wykorzystanie tego systemu w projekcie wiąże się z jakimś narzutem. Jeśli realizowany przez Ciebie projekt nie ma skrajnych wymagań co do wydajności, na pewno możesz sobie pozwolić na jego użycie. Zalety znacznie przewyższają wady.

Twig dla twórców szablonów

Zajmijmy się składnią omawianego systemu szablonów. Moim zdaniem jest bardzo przejrzysta. Nic dziwnego, że podobnej semantyki możesz się spodziewać również przy innych systemach/frameworkach (Phalcon, Angular JS, …). To co mi od razu się spodobało to bardzo zwięzły zapis, niektórych wyrażeń (nawet w porównaniu do php z włączonymi short tag-ami):

<!DOCTYPE html>
<html>
    <head>
        <title>Baszczewski - Dobry programista freelancer</title>
    </head>
    <body>
        {# to jest komentarz #}

        Pracownik: {{ name }} {# tutaj w prosty sposób wstawiamy wartość zmiennej #}

        {% for task in tasks %}
            <li><a href="tasks/{{ task.id }}">{{ task.title }}</a></li>
        {% endfor %}
    </body>
</html>

Rozważmy kilka przykładów. Nawiasy {{ nazwa_zmiennej|filtr_zmiennej~tutaj_metoda() }} umożliwiają w ich obrębie umieszczanie wartości zmiennych bądź wywołania metod.

{# umieszczenie dwóch różnych zmiennych i oddzielenie ich spacją #}
{{ imie~' '~nazwisko }} 

{# użycie filtra zmieniającego ciąg znaków na małe litery; rezultat: "witaj" #}
{{ 'WITAJ'|lower }} 

{# zwrócenie ilości elementów w tablicy / znaków w tekście #}
{{ nazwisko|length }}

{# przykład użycia metody random -> przykładowy rezultat 6 #}
{{ random(10) }} 

{# inny przykład użycia metody random -> przykładowy rezultat "Gdańsk" #}
{{ random(['Gdańsk', 'Radom', 'Warszawa']) }}

{# filtr użyty na dłuższym fragmencie tekstu #}
{% filter upper %}
    ten tekst będzie pisany dużymi literami
{% endfilter %}

Twig posiada bardzo przyjazny sposób zapisu instrukcji sterujących.

{# instrukcja sterująca if #}
{% if jakas_zmienna %}
    tak, zmienna jest zadeklarowa i posiada wartość różną od logicznego false
{% else %}
    nie zadeklarowano zmiennej bądź jej wartość jest równa false
{% endif %}

{# możesz również korzystać z dodatkowych operatorów podczas wyświetlania wartości #}
{{ zatrudniono ? 'zwolnij' : 'zatrudnij' }}

{# przykład łączący kilka technik #}
{% if pracownicy|length > 0 %}
    <ul>
        {% for pracownik in pracownicy %}
            <li>{{ pracownik.nazwisko }}</li>
        {% endfor %}
    </ul>
{% endif %}

Tutaj drobne wyjaśnienie. W pętli pojawił się pracownik, a po kropce podaliśmy nazwisko. Czym na wejściu był pracownik? Obiektem czy może tablicą? Odpowiedź: to bez znaczenia. Twig w pierwszej kolejności sprawdza czy to element tablicy, właściwość obiektu, jego metoda i tak dalej (jeśli chcesz zapoznać się ze szczegółami odsyłam do dokumentacji). To genialne w swojej prostocie rozwiązanie sprawia, że nie musisz się martwić o postać danych przed ich przekazaniem do widoków.

Na podsumowanie tego szybkiego przeglądu, przykład dziedziczenia szablonów:

{# plik szablonu, np. template.twig #}
<!DOCTYPE html>
<html>
    <head>
        {% block head %}
            <title>Baszczewski - {% block title %}{% endblock %}</title>
            <link rel="stylesheet" href="style.css"/>
        {% endblock %}
    </head>
    <body>
        <div id="content">{% block content %}{% endblock %}</div>
        <footer>
            {% block footer %}
                Tworzenie stron internetowych: programista php, html, css, js. 
            {% endblock %}
        </footer>
    </body>
</html>

{# podstrona wykorzystująca szablon, np. index.twig #}
{% extends "template.twig" %}

{% block title %}Dobry programista freelancer{% endblock %}
{% block head %}
    {{ parent() }}
    <style type="text/css">
        #content { color: #333; }
    </style>
{% endblock %}
{% block content %}
    <h1>Programista WWW</h1>
    <p>
        Tutaj treść podstrony.
    </p>
{% endblock %}

Powyższy przykład realizuje rozszerzenie szablonu zadeklarowanego w pliku template.twig. W takim szablonie deklarujemy bloki, które przy dziedziczeniu mogą być zastąpione, bądź rozszerzone. Przykładowo w pliku index.twig fragment head został użyty ponownie (metoda parent) wraz z dopisaniem stylu kaskadowego. Bloków, których w żaden sposób nie chcieliśmy modyfikować w docelowym szablonie już nie wymienialiśmy (stopka).

Jest to tylko zalążek umożliwiający zapoznanie się w jakim stylu pracuje się z systemem Twig. Zalecam zapoznanie się z dokumentacją w celu poznania wszystkich zaimplementowanych filtrów, metod oraz tagów.

Twig dla programistów

Jesteś zdecydowany skorzystać z Twig-a w swoim projekcie? Zobacz jakie to proste.

<?php

// załadowanie biblioteki
require_once '/katalog/do/lib/Twig/Autoloader.php';

Twig_Autoloader::register();

$loader = new Twig_Loader_Filesystem('/katalog/do/szablonow');
$twig = new Twig_Environment($loader, array(
    'cache' => '/katalog/z/buforem_cache',
));

To wszystko? No powinieneś jeszcze przekazać zmienne do określonego widoku i go wyrenderować:

<?php

$template = $twig->loadTemplate('index.twig');
echo $template->render(array('imie' => 'Marcin', 'nazwisko' => 'Baszczewski', 'zawod' => 'programista'));

Pamiętaj, że to nie wszystkie możliwości. Twig posiada naprawdę dużo opcji konfiguracyjnych. Do tego możliwe jest tworzenie własnych rozszerzeń. Dzięki wtyczkom możesz wzbogacić składnię Twig-a o własne metody, filtry, tagi bądź sprawić by miał dostęp do widoków dostępnych z nietypowych źródeł.

Kohana oraz Twig – Integracja

Na potrzeby jednego z moich projektów przygotowałem rozszerzenie dla Kohany, umożliwiające korzystanie z widoków Twig-a. Jest to bardzo elastyczne rozwiązanie. Absolutnie nic nie musisz zmieniać w swoich dotychczasowych skryptach. Jeśli chodzi o cześć programistyczną z widoków korzystasz identycznie jak dotychczas. Wszystko sprowadza się do podania innej ścieżki do widoku, np. View::factory(‘Template.twig’). Jeśli w danej aplikacji chciałbyś skorzystać z klasycznego widoku php nadal masz taką możliwość. Po prostu w ścieżce nie podawaj rozszerzenia *.twig, a automatycznie zostanie użyty klasyczny system. Jeśli w swojej aplikacji nie będziesz używał żadnego z widoków Twig-a jego klasy w żaden sposób nie będą wczytywane i nie wpłyną na wydajność Twojego skryptu.

<?php defined('SYSPATH') OR die('No direct script access.');
/**
 * Rozszerzenie klasy widoków. Zapewnienie wsparcia dla Twig-a.
 *
 * @package    Kohana
 * @category   Base
 * @author     Marcin Baszczewski (baszczewski.pl)
 * @copyright  (c) 2013 Marcin Baszczewski
 */
class View extends Kohana_View 
{
    protected static $_twig_environment = null;

    public function set_filename($file)
    {
        $ext = pathinfo($file, PATHINFO_EXTENSION);
        $name = pathinfo($file, PATHINFO_FILENAME);

        if ($ext===Kohana::$config->load('twig.suffix')) 
        {
            $path = $file;
        }
        else
        {
            if (($path = Kohana::find_file('views', $file)) === FALSE)
            {
                throw new View_Exception('The requested view :file could not be found', array(
                    ':file' => $file,
                ));
            }
        }

        // przechowujemy ścieżkę lokalnie
        $this->_file = $path;

        return $this;
    }

    protected static function capture($kohana_view_filename, array $kohana_view_data)
    {
        $ext = strtolower(pathinfo($kohana_view_filename, PATHINFO_EXTENSION));
        
        if ($ext===Kohana::$config->load('twig.suffix'))
        {
            if (View::$_twig_environment===null)
            {
                // ładujemy bibliotekę
                require_once Kohana::find_file('vendor', 'twig/lib/Twig/Autoloader');

                // aktywujemy
                Twig_Autoloader::register();

                $paths = array();

                // udostępniamy Twig-owi określone ścieżki
                // w których ten znajdzie widoki
                // można to również osiągnąć poprzez rozszerzenie loader-a
                foreach(Kohana::include_paths() as $path)
                {
                    $path.='views';
                    if (is_dir($path))
                    {
                        $paths[] = $path;
                    }
                }

                $loader = new Twig_Loader_Filesystem($paths);

                View::$_twig_environment = new Twig_Environment($loader, Kohana::$config->load('twig.environment'));
                
                // wczytujemy rozszerzenia Twig-a
                foreach (Kohana::$config->load('twig.extensions') as $extension)
                {
                    View::$_twig_environment->addExtension(new $extension);
                }
            }

            return View::$_twig_environment->render($kohana_view_filename, $kohana_view_data);
        }

        return parent::capture($kohana_view_filename, $kohana_view_data);
    }
}

Plik konfiguracyjny:

<?php defined('SYSPATH') OR die('No direct access allowed.');

return array
(
    'environment' => array
    (
        'debug'               => FALSE,
        'trim_blocks'         => FALSE,
        'charset'             => 'utf-8',
        'base_template_class' => 'Twig_Template',
        'cache'               => TMPPATH.'views',
        'auto_reload'         => TRUE,
        'strict_variables'    => FALSE,
        'autoescape'          => TRUE,
        'optimizations'       => -1,
    ),
    'extensions' => array
    (
    ),
    'suffix'         => 'twig',
    'context_object' => TRUE,
);

Komentarze

Brak komentarzy. Może dodasz pierwszy?

Chcę dodać komentarz