Twig w praktyce

by Mateusz Tymek — on Zend Framework, PHP

Head's up! This post was written back in 2011 and is very likely to contain outdated information.

Wtyczka do NetBeans kolorująca składnię plików .twig

Jednym z powodów dla którego postanowiłem stworzyć tą stronę własnoręcznie (zamiast sięgnąć po gotowce typu WordPress), była chęć posiadania pewnego rodzaju "poligonu doświadczalnego". Pozwala mi to sprawdzać różne rozwiązania w praktyce, zanim będę mógł "sprzedać je" klientom. Po napisaniu artykułu o Twigu postanowiłem poświęcić trochę czasu i wdrorzyć go na tej stronie. Zadanie okazało się dosyć łatwe i pozwoliło mi zapoznać się z tą technologią.
W tym poście zebrałem kilka przykładów i porównań między szablonami w czystym PHP a Twigiem. 

Zwig

W poprzednim wpisie pokazałem jak samodzielnie zintegrować Twiga z projektem napisanym w ZF. Takie podejście jest bardzo fajne pod względem edukacyjnym, jednak przed przystąpieniem do poważniejszego projektu zawsze warto sprawdzić, czy nie wymyślamy koła na nowo. W tym przypadku znalazłem świetną bibliotekę - Zwig (https://github.com/arnaud-lb/Zwig). Jej największa zaletą jest transparentna obsługa helperów widoku - zarówno tych Zendowych, jak i własnych. Wrócę do tego za chwilę, najpierw pokażę kod (metodę w Bootstrapie) którym spiąłem moją aplikację z Twigiem:

protected function _initView()
{
    $view = new Zwig_View(array(
        'encoding' => 'UTF-8'
    ));

    $loader = new Twig_Loader_Filesystem(array());
    $zwig = new Zwig_Environment($view, $loader, array(
        'cache' => APPLICATION_PATH . '/../data/cache/twig/',
        'auto_reload' => true,
    ));

    $view->setEngine($zwig);
    $view->doctype(Zend_View_Helper_Doctype::XHTML1_STRICT);
    $view->addScriptPath(APPLICATION_PATH . '/layouts/scripts');

    $view->user = Zend_Auth::getInstance()->getIdentity();

    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view, array(
        'viewSuffix' => 'twig',
    ));
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);

    return $view;
}

Breadcrumby

 Na początek tak zwane "okruszki chleba". Chcemy uzyskać mniej więcej taki efekt:

   Strona główna >> Motocykle >> Suzuki

Niech pierwsze dwa elementy będą linkami. Definiujemy tablicę (np w kontrolerze) i przekazujemy ją do widoku:

$this->view->breadcrumbs = array(
    array(
        'url' => '/',
        'title' => 'Strona Główna'
    ),
    array(
        'url' => '/motocykle',
        'title' => 'Motocykle'
    ),
    array(
        'title' => 'Suzuki'
    ),
);

<div id="breadcrumbs">
    {% for breadcrumb in breadcrumbs %}
        {% if breadcrumb.url is defined %}
        <span>
            <a title="{{ breadcrumb.title }}" href="{{ breadcrumb.url }}">{{ breadcrumb.title }}</a>
        </span>
        {% else %}
        <span title="{{ breadcrumb.title }}">
        {{ breadcrumb.title }}
        </span>
        {% endif %}
        {% if not loop.last %}
        &raquo;
        {% endif %}
    {% endfor %}
</div>

Gettery

Jeżeli dany obiekt nie posiada publicznych właściwości a dostęp do nich odbywa się przy poprzez funkcje typu getXXX(), Twig pozwala na skrócenie zapisu:

Szablony PHP

<?php echo $user->getName() ?>
   <?php echo $user->getEmail() ?>

Twig

{{ user.name }}
    {{ user.email }}

Paginacja

Zend_Paginator umożliwia określenie widoku który wyświetli listę stron. 

Szablony PHP

<?php if ($this->pageCount > 1): ?>
    <div class="paginationControl">
        <span>Idź do strony:</span>
        <?php foreach ($this->pagesInRange as $page) : ?>
                <?php if ($this->current == $page) : ?>
                    <?php echo $page ?>
                <?php else : ?>
                    <a href="<?php echo $this->url(array('page' => $page)) ?>">
                        <?php echo $page ?>
                    </a>
                <?php endif; ?>
        <?php endforeach; ?>
    </div>
    <?php endif; ?>

Twig

{% if pageCount > 1 %}
    <div class="paginationControl">
        <span>Idź do strony:</span>

        {% for page in pagesInRange %}
            {% if current == page %}
            {{ page }}
            {% else %}
            <a href="{{ url({'page': page}) }}">
                {{ page }}
            </a>
            {% endif %}
        {% endfor %}
    </div>
    {% endif %}

Formularze

Szablony PHP

<?php echo $this->registrationForm ?>

Twig

{{ registrationForm | raw }}

 

Ustawianie zmiennych

Zdarza się że w widoku wyświetlamy coś kilka razy (np link do artykułu).

Szablony PHP

<?php $link = $this->url(array('slug' => $this->article['slug']), 'view-article'); ?>
    <?php echo $link ?>

Twig

{% set link = url({'slug' : article.slug}, 'view-article') %}
    {{ link }}

Helpery widoku

 

Jak napisałem wyżej, po skonfigurowaniu Zwig pozowala na natychmiastowe wykorzystanie wszystkich pomocników widoku - także tych zdefiniowanych przez nas. 
Jakiś czas temu popełniłem artykuł na temat tworzenia menu z ikonkami (/blog/zend-navigation-menu-z-ikonkami) na bazie Zend_Navigation. W backendzie bloga używałem czegoś takiego:

<div id="top-menu">
        <?php echo $this->iconMenu($this->navigation()->findById('main'))
                        ->setIconPath($this->baseUrl('/admin/images/icons')) ?>
    </div>

Zapis Twigowy jest bardzo podobny:

<div id="top-menu">
        {{ iconMenu(navigation().findById('main')).setIconPath(baseUrl('/admin/images/icons')) | raw }}
    </div>

For, else, endfor

Mała rzecz a cieszy. Do tej pory lista użytkowników wyglądała tak:

<?php if (count($this->users)) :
    foreach ($this->users as $user) : ?>
    <tr>
        <td>
            <?php echo $user->getName() ?>
        </td>
        .......
    </tr>
    <?php endforeach ?>
<?php else : ?>
    Brak użytkowników do wyświetlenia.
<?php endif ?>

Twig jest bardziej elegancki:

{% for user in users %}
    <tr>
        <td>
            {{ user.name }}
        </td>
        .......
    </tr>
    {% else %}
    Brak użytkowników do wyświetlenia.
    {% endfor %}

To tyle jeśli chodzi o przykłady. Muszę przyznać że koncepcja szablonów bardzo mi się podoba, mimo iż do niedawna byłem ich przeciwnikiem. No ale w  programowaniu rozwój polega na ciągłym szukaniu nowych ścieżek, a przez uparte trzymanie się jednej drogi człowiek szybko przestaje być na bieżąco.


comments powered by Disqus