Создание и использование шаблонов

Как уже было написано в предыдущей статьe, контроллеры отвечают за обработку каждого запроса, который поступает в приложение Symfony, и обычно заканчивают отображением шаблона для генерирования содержимого ответа

На самом деле, контроллер передает большую часть трудоемких операций другим частям фреймворка, чтобы код можно было тестировать и повторно использовать. Когда контроллеру необходимо создать HTML, CSS или любой другой контент, он передает эту работу шаблонизатору.

В этой статье вы научитесь писать мощные шаблоны, которые можно использовать для возвращения контента пользователю, создания тела имейлов и многого другого. Также вы научитесь использовать шорткаты, узнаете хитрые способы наследования шаблонов и повторного использования кода шаблона.

Шаблоны

Шаблон - это обычный текстовый файл, который может генерировать любой формат, который основывается на тексте (HTML, XML, CSV, LaTeX и др.). Самый известный тип шаблона это PHP шаблон - текстовый файл, который обрабатывается PHP и содержит как текст, так и PHP-код:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html>
    <head>
        <title>Welcome to Symfony!</title>
    </head>
    <body>
        <h1><?php echo $page_title ?></h1>

        <ul id="navigation">
            <?php foreach ($navigation as $item): ?>
                <li>
                    <a href="<?php echo $item->getHref() ?>">
                        <?php echo $item->getCaption() ?>
                    </a>
                </li>
            <?php endforeach ?>
        </ul>
    </body>
</html>

Преимущество Symfony в том, что в ёё состав входит еще более мощный язык шаблонов, который называется Twig. С помощью Twig вы можете писать лаконичные и читабельные шаблоны, которые удобнее в использовании для веб-дизайнеров, не говоря уже о том, что этот шаблон во многих случаях функциональнее чем PHP шаблоны:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<!DOCTYPE html>
<html>
    <head>
        <title>Welcome to Symfony!</title>
    </head>
    <body>
        <h1>{{ page_title }}</h1>

        <ul id="navigation">
            {% for item in navigation %}
                <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
            {% endfor %}
        </ul>
    </body>
</html>

Twig содержит три типа специальных синтаксических конструкций:

{{ ... }}
“Что-то сказать”: печатает переменную или результат какого-то выражения в шаблоне.
{% ... %}
“Что-то выполнить”: тег (tag), который контролирует логику шаблона; используется для выполнения таких выражений, как например, циклы for-loops.
{# ... #}
“Что-то прокомментировать”: это эквивалент синтаксису PHP /* comment */. Этот синтаксис используется для добавления одно- или многострочных комментариев. Содержимое комментария не включается в отображаемые страницы.

Twig также содержит фильтры, которые изменяют контент перед тем, как его отобразить. Следующий фильтр позволяет перевести переменную title в верхний регистр перед тем, как отобразить ее:

1
{{ title|upper }}

Twig по умолчанию содержит много тегов (tags), фильтров (filters) и функций (functions), которые доступны по умолчанию. Вы даже можете добавлять свои собственные расширения в Twig с помощью Расширения Twig.

Код Twig будет выглядеть схоже с PHP-кодом, с некоторыми маленькими, но приятными различиями. Следующий пример использует стандартный тег for и функцию cycle() для отображения десяти тегов div с переменными классами odd и even:

1
2
3
4
5
{% for i in 1..10 %}
    <div class="{{ cycle(['even', 'odd'], i) }}">
      <!-- some HTML here -->
    </div>
{% endfor %}

На протяжении этой статьи, примеры шаблонов будут показаны как в Twig так и на PHP.

Шаблоны Twig изначально задумывались как более простые, и не обрабатывают PHP код. Это сделано намеренно: система шаблонов Twig задумана для быстрого создания уровней представления, а не для программной логики. Чем больше вы будете использовать Twig, тем больше вы будете замечать преимущества такого разделения. И, конечно, вас будут боготворить все дизайнеры.

Twig также умеет то, что неподвластно PHP, например, контролировать символы пробела; “играть в песочнице”(sandboxing) - изолировать на время выполнения загружаемый код в ограниченную среду; вручную, в зависимости от контекста, управлять выводимыми данными; расширять пользовательские функции и фильтры, которые влияют только на шаблоны. У Twig много небольших особенностей, которые делают процесс написание шаблонов проще и лаконичнее. Посмотрим на пример ниже, в котором цикл комбинируется с логическим выражением if:

1
2
3
4
5
6
7
<ul>
    {% for user in users if user.active %}
        <li>{{ user.username }}</li>
    {% else %}
        <li>No users found</li>
    {% endfor %}
</ul>

Кеширование шаблонов в Twig

Twig быстр, так как каждый шаблон компилируется в обычный PHP класс, который отображается во время выполнения. Но не волнуйтесь: это происходит автоматически, и*вам* не нужно ничего для этого делать. И в то время, как вы будете программировать, Twig настолько умен, что он будет рекмопилировать ваши шаблоны после любого внесенного изменения. Это значит, что Twig быстр в производстве, но легок в использовании при программировании.

Наследование шаблонов и макетов страницы (layouts)

Зачастую у шаблонов в проекте есть общие элементы, такие как “заголовок” (header), “подвал” (footer), боковые панели (sidebar) и т.п. В Symfony эта задача решается по-другому: шаблон может быть оформлен с помощью другого шаблона. Принцип работы здесь точно такой же, как с PHP-классами: наследование шаблонов позволяет вам выстроить базовый шаблон “макета страницы” (layout template), который содержит все необходимые вам общие элементы сайта, которые еще называют блоками (аналогично с “PHP-классом с базовыми методами”). Дочерний шаблон можно расширить по сравнению с базовым, а также можно переопределять любые его блоки (аналогично с “дочерним PHP-классом, который переопределяет некоторые методы родительского класса”).

Для начала создайте файл базового макета страницы:

  • Twig
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    {# app/Resources/views/base.html.twig #}
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title>{% block title %}Test Application{% endblock %}</title>
        </head>
        <body>
            <div id="sidebar">
                {% block sidebar %}
                    <ul>
                        <li><a href="/">Home</a></li>
                        <li><a href="/blog">Blog</a></li>
                    </ul>
                {% endblock %}
            </div>
    
            <div id="content">
                {% block body %}{% endblock %}
            </div>
        </body>
    </html>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <!-- app/Resources/views/base.html.php -->
    <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title><?php $view['slots']->output('title', 'Test Application') ?></title>
        </head>
        <body>
            <div id="sidebar">
                <?php if ($view['slots']->has('sidebar')): ?>
                    <?php $view['slots']->output('sidebar') ?>
                <?php else: ?>
                    <ul>
                        <li><a href="/">Home</a></li>
                        <li><a href="/blog">Blog</a></li>
                    </ul>
                <?php endif ?>
            </div>
    
            <div id="content">
                <?php $view['slots']->output('body') ?>
            </div>
        </body>
    </html>
    

Note

Несмотря на то, что обсуждение наследования шаблонов будет вестись применительно к Twig, в Twig и PHP-шаблонах используется одна и та же философия.

Этот шаблон определяет базовый костяк HTML-документа для простой страницы с двумя колонками. В данном примере определены три области {% block %} (title, sidebar и body). Каждый блок может быть переопределен дочерним шаблоном или оставлен с реализацией по умолчанию. Также этот шаблон можно отобразить напрямую. В таком случае блоки title, sidebar и body просто сохранят свои значения по умолчанию, использованные в этом шаблоне.

Дочерний шаблон может выглядеть, например, так:

  • Twig
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    {# app/Resources/views/blog/index.html.twig #}
    {% extends 'base.html.twig' %}
    
    {% block title %}My cool blog posts{% endblock %}
    
    {% block body %}
        {% for entry in blog_entries %}
            <h2>{{ entry.title }}</h2>
            <p>{{ entry.body }}</p>
        {% endfor %}
    {% endblock %}
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <!-- app/Resources/views/blog/index.html.php -->
    <?php $view->extend('base.html.php') ?>
    
    <?php $view['slots']->set('title', 'My cool blog posts') ?>
    
    <?php $view['slots']->start('body') ?>
        <?php foreach ($blog_entries as $entry): ?>
            <h2><?php echo $entry->getTitle() ?></h2>
            <p><?php echo $entry->getBody() ?></p>
        <?php endforeach ?>
    <?php $view['slots']->stop() ?>
    

Note

Родительский шаблон определяется благодаря специальному синтаксису строковой последовательности (base.html.twig). Этот путь относится к каталогу проекта``app/Resources/views``. Вы также можете использовать эквивалент логического имени ::base.html.twig. Такое соглашение о присвоении имен полностью объясняется в разделе Правила именования и расположения шаблонов template-naming-locations.

Ключ к наследованию шаблонов - это тег {% extends %}. Он сообщает движку шаблонизатора, что сначала необходимо выполнить базовый шаблон, который настроит общую разметку и определит несколько блоков. После этого начинает отображаться дочерний шаблон, при этом блоки родителя``title`` и body замещаются аналогичными блоками потомка. В зависимости от значения переменной blog_entries, результат может выглядеть так:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>My cool blog posts</title>
    </head>
    <body>
        <div id="sidebar">
            <ul>
                <li><a href="/">Home</a></li>
                <li><a href="/blog">Blog</a></li>
            </ul>
        </div>

        <div id="content">
            <h2>My first post</h2>
            <p>The body of the first post.</p>

            <h2>Another post</h2>
            <p>The body of the second post.</p>
        </div>
    </body>
</html>

Обратите внимание, что дочерний шаблон не определяет блок sidebar, вместо этого используется значение из родительского шаблона. Содержимое тега {% block %} в родительском шаблоне всегда используется по умолчанию.

Tip

Вы можете использовать столько уровней наследования, сколько вам потребуется. См. Наследование шаблонов для более детальной информации.

Когда вы работаете с наследованием шаблона нужно следовать нескольким правилам:

  • Если вы используете в шаблоне тег {% extends %}, он должен быть первым тегом в этом шаблоне;

  • Чем больше тегов {% block %} в вашем базовом шаблоне, тем лучше. Запомните, дочерний шаблон не обязательно реализовывает все блоки родительского шаблона, так что создавайте необходимое количество блоков в вашем базовом шаблоне и указывайте для них разумный контент по умолчанию. Чем больше блоков будет в вашем базовом шаблоне, тем гибче будет макет вашей страницы;

  • Если вы обнаружите повторяющийся контент в нескольких шаблонах, скорее всего это значит, что вам нужно переместить этот контент в тег {% block %} родительского шаблона. В некоторых случаях лучшим решением будет переместить содержимое на новый шаблон и include его (см. Подключение других шаблонов including-templates );

  • Если вам нужно содержимое блока из родительского шаблона, вы можете использовать функцию {{ parent() }}. Это удобно, если вам надо добавить что-то к содержанию родительского блока, а не полностью заменить его:

    1
    2
    3
    4
    5
    6
    7
    {% block sidebar %}
        <h3>Table of Contents</h3>
    
        {# ... #}
    
        {{ parent() }}
    {% endblock %}
    

Правила именования и расположения шаблонов

По умолчанию, шаблоны могут располагаться в двух различных местах:

app/Resources/views/
Каталог приложения views может содержать общие для всего приложения шаблоны (например, макет страницы вашего приложения и шаблоны пакета приложения), также как и шаблоны, которые переопределяют сторонние шаблоны пакетов (см. Переопределение шаблонов пакетов).
vendor/path/to/CoolBundle/Resources/views/
Каждый сторонний пакет содержит свои шаблоны в каталоге Resources/views/ (и ее подкаталогах). Когда вы захотите поделиться (share) своим пакетом, вам стоит расположить шаблоны внутри пакета, а не в каталоге app/.

Большинство шаблонов, которые вы будете использовать, располагаются в каталоге a app/Resources/views/. Путь, который вы будете использовать, будет родственным этому каталогу. Для примера, чтобы отобразить/наследовать app/Resources/views/base.html.twig, вам нужно будет использовать путь base.html.twig. А чтобы отобразить/наследовать app/Resources/views/blog/index.html.twig, вам нужно будет использовать путь blog/index.html.twig.

Отсылка к шаблонам внутри пакета

Если вам нужно сослаться на шаблон, который находится в пакете, Symfony использует специальный синтаксис строковой последовательности bundle:directory:filename. Это предусматривает несколько типов шаблонов, каждый из которых расположен в определенном месте:

  • AcmeBlogBundle:Blog:index.html.twig: Эта форма записи для того, чтобы определить шаблон для конкретной страницы. Три части синтаксиса, каждая из которых отделяется двоеточием (:), означают следующее:

    • AcmeBlogBundle: (пакет) шаблон расположен внутри пакета AcmeBlogBundle (например, src/Acme/BlogBundle);
    • Blog: (каталог) указывает, что шаблон расположен внутри подкаталога Blog каталога Resources/views;
    • index.html.twig: (имя файла) собственно имя файла это index.html.twig.

    При условии, что AcmeBlogBundle расположен в src/Acme/BlogBundle, полный путь к макету страницы будет src/Acme/BlogBundle/Resources/views/Blog/index.html.twig.

  • AcmeBlogBundle::layout.html.twig: Этот синтаксис отсылает нас к базовому шаблону для определенного пакета AcmeBlogBundle. Поскольку в середине отсутствует наименование “каталога” (например, Blog), шаблон находится в каталоге Resources/views/layout.html.twig пакета AcmeBlogBundle. Да, вам не показалось - внутри синтаксиса два двоеточия, как раз в том месте, где отсутствует подкаталог “контроллер”.

В разделе Переопределение шаблонов пакетов вы узнаете, как любой шаблон, который, для примера, находится в пакете AcmeBlogBundle, можно переопределить путем размещения шаблона с тем же именем в каталоге app/Resources/AcmeBlogBundle/views/. Это дает возможность переопределять шаблоны из любого стороннего пакета.

Tip

Надеемся, что синтаксис именования шаблонов оказался вам знаком - такое же соглашение об именовании используется и для контроллеров (см. Шаблон именования контроллера controller-string-syntax).

Суффиксы шаблонов

У каждого имени шаблона есть два расширения, которые указывают на формат и тип дивжок (engine) для этого шаблона.

Имя файла Формат Движок
blog/index.html.twig HTML Twig
blog/index.html.php HTML PHP
blog/index.css.twig CSS Twig

По умолчанию любой шаблон в Symfony может быть написан либо на Twig, либо на PHP. Последняя часть расширения (например, .twig или .php) указывает, какой из этих двух движков будет использован. Первая часть расширения (например, .html, .css, и т.д.) это конечный формат, который будет генерировать шаблон. В отличие от типа шаблонизатора, который определяет, как Symfony будет анализировать шаблон, указание формата это всего лишь способ организации шаблонов, на тот случай, если один и тот же ресурс можно отобразить и как HTML (index.html.twig), и как XML (index.xml.twig) и в любом другом формате. Дополнительная информация содержится в разделе Форматы шаблонов.

Теги и Помощники

Вы уже разбираетесь в основах создания шаблонов, способах именования и наследования шаблонов. Самое сложное уже позади. В этом разделе вы узнаете о множестве инструментов, которые помогают выполнить типичные задачи при работе с шаблонами, например, такие как подключение других шаблонов, создание ссылок на страницу и вставка изображений.

Symfony содержит несколько специализированных тегов и функций Twig, которые упрощают работу дизайнера шаблонов. В PHP шаблонизатор предоставляет расширяемую систему помощников, которая предоставляет полезные функции в контексте шаблонов.

Вы уже видели несколько встроенных в Twig тегов ({% block %} и {% extends %}), а также пример PHP-хелпера ($view['slots']). Теперь вы узнаете и о некоторых других.

Подключение других шаблонов

У вас часто будет возникать потребность подключить один и тот же шаблон или фрагмент кода для нескольких страниц. Например, в приложении с «новыми статьями», код шаблона, отображающий статью, может быть использован на странице детальной информации о статье, на странице, отображающей наиболее популярные статьи или же на странице со списком последних статей.

Когда вам необходимо использовать некую часть PHP-кода повторно, вы переносите этот код в новый PHP-класс или в функцию. То же верно и для шаблонов. Переместив повторно используемый код шаблона в собственный шаблон, он впоследствии может быть подключен к любому другому шаблону. Сначала создайте шаблон, который хотите использовать многократно.

  • Twig
    1
    2
    3
    4
    5
    6
    7
    {# app/Resources/views/article/article_details.html.twig #}
    <h2>{{ article.title }}</h2>
    <h3 class="byline">by {{ article.authorName }}</h3>
    
    <p>
        {{ article.body }}
    </p>
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    <!-- app/Resources/views/article/article_details.html.php -->
    <h2><?php echo $article->getTitle() ?></h2>
    <h3 class="byline">by <?php echo $article->getAuthorName() ?></h3>
    
    <p>
        <?php echo $article->getBody() ?>
    </p>
    

Подключить этот шаблон к любому другому несложно:

  • Twig
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    {# app/Resources/views/article/list.html.twig #}
    {% extends 'layout.html.twig' %}
    
    {% block body %}
        <h1>Recent Articles<h1>
    
        {% for article in articles %}
            {{ include('article/article_details.html.twig', { 'article': article }) }}
        {% endfor %}
    {% endblock %}
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    <!-- app/Resources/article/list.html.php -->
    <?php $view->extend('layout.html.php') ?>
    
    <?php $view['slots']->start('body') ?>
        <h1>Recent Articles</h1>
    
        <?php foreach ($articles as $article): ?>
            <?php echo $view->render(
                'Article/article_details.html.php',
                array('article' => $article)
            ) ?>
        <?php endforeach ?>
    <?php $view['slots']->stop() ?>
    

Шаблон подключается при помощи функции {{ include() }}. Обратите внимание, что имя шаблона следует типовым конвенциям об именовании. Шаблон article_details.html.twig использует переменную article, которую вы передаете в него. В этом случае, вы можете полностью избежать этого, так как все переменные, доступные в list.html.twig также доступны в article_details.html.twig (если только вы не установили with_context в значение false).

Tip

Синтаксис {'article': article} - это стандартный синтаксис для хэшей (т.е. массивов с названными ключами) в Twig. Если вам нужно передать много элементов, массив будет выглядеть следующим образом: {'foo': foo, 'bar': bar}.

Ссылки на страницы

Создание ссылок на другие страницы в вашем приложении - это одна из типичных операций в шаблоне. Вместо того, чтобы хардкодить URL в шаблоне, используйте Twig-функцию path (в PHP-помощьник router) для создания URL, основанных на конфигурации маршрутизатора. Потом, если вы захотите изменить URL конкретной страницы, вам всего лишь потребуется изменить конфигурацию маршрутизатора. Шаблоны автоматически сгенерируют новый URL.

Сначала создайте ссылку на страницу "welcome", которая определяется следующей конфигураций маршрутизатора:

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    // src/AppBundle/Controller/WelcomeController.php
    
    // ...
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    
    class WelcomeController extends Controller
    {
        /**
         * @Route("/", name="welcome")
         */
        public function indexAction()
        {
            // ...
        }
    }
    
  • YAML
    1
    2
    3
    4
    # app/config/routing.yml
    welcome:
        path:     /
        defaults: { _controller: AppBundle:Welcome:index }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <!-- app/config/routing.yml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing
            http://symfony.com/schema/routing/routing-1.0.xsd">
    
        <route id="welcome" path="/">
            <default key="_controller">AppBundle:Welcome:index</default>
        </route>
    </routes>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    // app/config/routing.php
    use Symfony\Component\Routing\Route;
    use Symfony\Component\Routing\RouteCollection;
    
    $collection = new RouteCollection();
    $collection->add('welcome', new Route('/', array(
        '_controller' => 'AppBundle:Welcome:index',
    )));
    
    return $collection;
    

Для создания ссылки на страницу просто используйте функцию path() и сошлитесь на маршрут:

  • Twig
    1
    <a href="{{ path('welcome') }}">Home</a>
    
  • PHP
    1
    <a href="<?php echo $view['router']->path('welcome') ?>">Home</a>
    

Как и ожидалось, это сгенерирует URL /. Давайте теперь посмотрим, как это работает для более сложных маршрутов:

  • Annotations
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    // src/AppBundle/Controller/ArticleController.php
    
    // ...
    use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
    
    class ArticleController extends Controller
    {
        /**
         * @Route("/article/{slug}", name="article_show")
         */
        public function showAction($slug)
        {
            // ...
        }
    }
    
  • YAML
    1
    2
    3
    4
    # app/config/routing.yml
    article_show:
        path:     /article/{slug}
        defaults: { _controller: AppBundle:Article:show }
    
  • XML
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    <!-- app/config/routing.xml -->
    <?xml version="1.0" encoding="UTF-8" ?>
    <routes xmlns="http://symfony.com/schema/routing"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://symfony.com/schema/routing
            http://symfony.com/schema/routing/routing-1.0.xsd">
    
        <route id="article_show" path="/article/{slug}">
            <default key="_controller">AppBundle:Article:show</default>
        </route>
    </routes>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    // app/config/routing.php
    use Symfony\Component\Routing\Route;
    use Symfony\Component\Routing\RouteCollection;
    
    $collection = new RouteCollection();
    $collection->add('article_show', new Route('/article/{slug}', array(
        '_controller' => 'AppBundle:Article:show',
    )));
    
    return $collection;
    

В этом случае, вам нужно указать как имя маршрута (article_show), так и значение параметра {slug}. Используя этот маршрут, вернитесь к шаблону recent_list.html.twig из предыдущего раздела, и создайте ссылку на статью правильно:

  • Twig
    1
    2
    3
    4
    5
    6
    {# app/Resources/views/article/recent_list.html.twig #}
    {% for article in articles %}
        <a href="{{ path('article_show', {'slug': article.slug}) }}">
            {{ article.title }}
        </a>
    {% endfor %}
    
  • PHP
    1
    2
    3
    4
    5
    6
    7
    8
    <!-- app/Resources/views/Article/recent_list.html.php -->
    <?php foreach ($articles as $article): ?>
        <a href="<?php echo $view['router']->path('article_show', array(
            'slug' => $article->getSlug(),
        )) ?>">
            <?php echo $article->getTitle() ?>
        </a>
    <?php endforeach ?>
    

Tip

Вы также можете созадть абсолютный URL, использовав функцию Twig url():

  • Twig
    1
    <a href="{{ url('welcome') }}">Home</a>
    
  • PHP
    1
    2
    3
    4
    <a href="<?php echo $view['router']->url(
        'welcome',
        array()
    ) ?>">Home</a>
    

Ссылки на ресурсы (assets)

Шаблоны также часто ссылаются на картинки, JacaScript, страницы стилей и прочие ресурсы (здесь и далее вместо asset будет использован термин ресурс). Конечно, вы можете хардкодить пути к ресурсам (например, /images/logo.png), но Symfony предлагает использовать более динамичную функцию Twig asset() :

  • Twig
    1
    2
    3
    <img src="{{ asset('images/logo.png') }}" alt="Symfony!" />
    
    <link href="{{ asset('css/blog.css') }}" rel="stylesheet" />
    
  • PHP
    1
    2
    3
    <img src="<?php echo $view['assets']->getUrl('images/logo.png') ?>" alt="Symfony!" />
    
    <link href="<?php echo $view['assets']->getUrl('css/blog.css') ?>" rel="stylesheet" />
    

Главная задача функции asset() - сделать ваше приложение более мобильным. Если ваше приложение находится в корне хоста (например, http://example.com), тогда отображенный путь будет /images/logo.png. Но, если ваше приложение находится в подкаталоге (наприер, http://example.com/my_app), каждый путь к ресурсу должен отображаться с этим подкаталогом (например, /my_app/images/logo.png). Функция asset() берёт на себя заботу по определению того, как именно ваше приложение используется и генерирует соответствующие пути.

В дополнение к этому, если вы используете функцию asset(), Symfony сможет автоматически добавлять строку запроса к вашему ресурсу, чтобы гарантировать, что обновленные статичные ресурсы не будут загружены из кэша после выгрузки. Например, /images/logo.png может выглядеть как /images/logo.png?v2. Подробнее об этом смотрите версию опций конфигурации reference-framework-assets-version.

Если для ресурсов вам нужны абсолютные URL, используйте функцию Twig absolute_url() следующим образом:

1
<img src="{{ absolute_url(asset('images/logo.png')) }}" alt="Symfony!" />

Подключение CSS (таблиц стилей) и Javascript в Twig

Ни один современный сайт не может обойтись без подключения CSS и Javascript файлов. В Symfony, подключение этих ресурсов элегантно обрабатывается, опираясь на возможности наследования шаблонов.

Tip

В этом разделе вы узнаете философию подключения CSS и Javascript ресурсов в Symfony. Symfony также совместим с другой библиотекой, Assetic, которая следует той же философии, но позволяет вам выполнять намного более интересные операций над этими ресурсами. Дополнительную информацию можно получить в Как использовать Assetic для управления ресурсами.

Начните с добавления двух блоков к вашему базовому шаблону, который будет содержать ваши ресурсы: один назовите stylesheets, внутри HTML-тега head , другой - javascripts, перед закрывающим HTML-тегом body. Эти блоки будут содержать все таблицы стилей и скрипты, которые вам требуются для сайта:

  • Twig
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    {# app/Resources/views/base.html.twig #}
    <html>
        <head>
            {# ... #}
    
            {% block stylesheets %}
                <link href="{{ asset('css/main.css') }}" rel="stylesheet" />
            {% endblock %}
        </head>
        <body>
            {# ... #}
    
            {% block javascripts %}
                <script src="{{ asset('js/main.js') }}"></script>
            {% endblock %}
        </body>
    </html>
    
  • PHP
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    // app/Resources/views/base.html.php
    <html>
        <head>
            <?php ... ?>
    
            <?php $view['slots']->start('stylesheets') ?>
                <link href="<?php echo $view['assets']->getUrl('css/main.css') ?>" rel="stylesheet" />
            <?php $view['slots']->stop() ?>
        </head>
        <body>
            <?php ... ?>
    
            <?php $view['slots']->start('javascripts') ?>
                <script src="<?php echo $view['assets']->getUrl('js/main.js') ?>"></script>
            <?php $view['slots']->stop() ?>
        </body>
    </html>
    

Проще некуда! Но что, если вам потребуется включить дополнительную таблицу стилей или скрипт в дочернем шаблоне? Например, предположим, что у вас есть страница контактов и вам нужно подключить файл стилей contact.css только на одной этой странице. Внутри шаблона страницы contact необходимо выполнить следующее:

  • Twig
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    {# app/Resources/views/contact/contact.html.twig #}
    {% extends 'base.html.twig' %}
    
    {% block stylesheets %}
        {{ parent() }}
    
        <link href="{{ asset('css/contact.css') }}" rel="stylesheet" />
    {% endblock %}
    
    {# ... #}
    
  • PHP
    1
    2
    3
    4
    5
    6
    // app/Resources/views/contact/contact.html.twig
    <?php $view->extend('base.html.php') ?>
    
    <?php $view['slots']->start('stylesheets') ?>
        <link href="<?php echo $view['assets']->getUrl('css/contact.css') ?>" rel="stylesheet" />
    <?php $view['slots']->stop() ?>
    

В дочернем шаблоне вы просто переопределяете блок stylesheets и размещаете новый тег стиля внутри этого блока. Конечно же, поскольку вам нужно добавить стиль к блоку родительского контента, а не заменить его, вы должны использовать функцию Twig parent(), чтобы включить все стили из блока stylesheets базового шаблона.

Вы также можете включать ресурсы, расположенные в папке пакетов Resources/public. Вам нужно будет выполнить команду php php bin/console assets:install target [--symlink], которая скопирует (или создаст символическую ссылку) файлы в нужное место (target по умолчанию имеет значение "web".

1
<link href="{{ asset('bundles/acmedemo/css/contact.css') }}" rel="stylesheet" />

Конечным результатом является страница, которая включает как main.js, так и contact.css.

Отсылка к запросу, пользователю или сессии

Symfony также предоставляет вам глобальную переменную Twig app? которая может быть использована для доступа к текущему пользователю, запросу и др.

См. Переменная app для большей информации.

Экранирование

Twig выполняет автоматическое "экранирование" во время отображения какого-либо содержимого для того, чтобы защитить вас от атак Cross Site Scripting (XSS).

Представьте, что description равняется I <3 this product:

1
2
3
4
5
<!-- output escaping is on automatically -->
{{ description }} <!-- I &lt;3 this product -->

<!-- disable output escaping with the raw filter -->
{{ description|raw }} <!-- I <3 this product -->

Caution

PHP-шаблоны не экранируют контент автоматически.

Для того, чтобы узнать больше, см Экранирование.

Заключение

Шаблонизатор - это всего-лишь один из многих инструментов Symfony. И его работа проста: дать нам возможность отображать динамичный и сложный HTML-результат так, чтобы он в конечном счете возвращался к пользователю, отправлял email или делал что-то еще.

Продолжайте!

Перед тем, как снова нырнуть в мир Symfony, посмотрите Систему конфигурации.

Узнать больше

Эта документация является переводом официальной документации Symfony и предоставляется по свободной лицензии CC BY-SA 3.0.