Створення та використання шаблонів

Дата оновлення перекладу 2022-12-23

Створення та використання шаблонів

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

Мова шаблонів Twig

Мова шаблонів Twig дозволяє вам писати ємні та читані шаблони, які більш дружні по відношенню до веб-дизайнерів і, у багатьох сенсах, більш потужні, ніш шаблони PHP. Подивіться на наступний приклад шаблону Twig. Навіть якщо це перший раз, коли ви бачиті Twig, ви скоріше за все зрозумієте більшу частину:

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>

        {% if user.isLoggedIn %}
            Hello {{ user.name }}!
        {% endif %}

        {# ... #}
    </body>
</html>

Синтаксис Twig засновується на наступних трьох конструкціях:

  • {{ ... }}, використовується для відображення змісту змінної або результату оцінки виразу;
  • {% ... %}, використовується для виконання деякої логіки, на кшталт умовності або циклу;
  • {# ... #}, використовується для додавання коментарів у шаблон (на відміну від коментарів HTML, ці коментарі не додаються на відображувану сторінку).

Ви не можете запустити PHP-код всередині шаблінов Twig, але Twig надає утиліти для виконання деякої логіки у шаблонах. Наприклад, фильтри змінюють зміст до його відображення, наприклад, фільтр upper перетворює зміст у великі літери:

1
{{ title|upper }}

Twig постачається з довгим списком тегів, фільтрів та функцій, які доступні за замовчуванням. У додатках Symfony ви можете також використовувати ці фільтри та функції Twig, визначені Symfony і ви можете створювати власні функції та фільтри Twig.

Twig швидкий у середовищі prod (так як шаблони компілюються в PHP та кешуються автоматично), але зручніше використовувати середовище dev (тому що шаблони повторно компілюються автоматично при їх зміні).

Конфігурація Twig

Twig має декілька опцій конфігурації для визначення речей на кшталт формату, використовуваного для відображення цифр та дат, кешування шаблонів і т.д. Прочитайте довідник конфігурації Twig, щоб дізнатися про них більше.

Створення шаблонів

Перед детальним поясненням того, як створювати та відображати шаблони, подивіться на наступний приклад для швидкого розуміння всього процесу. Спочатку вам потрібно створити новий файл у каталозі templates/, щоб зберігати зміст шаблону:

1
2
3
{# templates/user/notifications.html.twig #}
<h1>Hello {{ user_first_name }}!</h1>
<p>У вас {{ notifications|length }} нових сповіщень.</p>

Потім, створіть контролер, який відображає цей шаблон та передає йому необхідні змінні:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// src/Controller/UserController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class UserController extends AbstractController
{
    // ...

    public function notifications(): Response
    {
        // отримати інформацію користувача та сповіщення якимось чином
        $userFirstName = '...';
        $userNotifications = ['...', '...'];

        // путь шаблона - это относительн путь файла из `templates/`
        return $this->render('user/notifications.html.twig', [
            // цей масив визначає змінні, передані шаблону, де ключ - це
            // ім'я змінної, а значення - значення змінної
            // (Twig рекомендує використання імен змінних snake_case : 'foo_bar' вместо 'fooBar')
            'user_first_name' => $userFirstName,
            'notifications' => $userNotifications,
        ]);
    }
}

Найменування шаблонів

Symfony рекомендує наступні імена шаблонів:

  • Використовуйте snake case для імен файлів та каталогів (наприклад, blog_posts.html.twig, admin/default_theme/blog/index.html.twig, і т.д.);
  • Визначте два розширення для імен файлів (наприклад, index.html.twig або blog_posts.xml.twig) у вигляді першого розширення (html, xml, і т.д.), фінальний формат якого генеруватиме шаблон.

Хоча шаблони зазвичай генерують зміст HTML, вони можуть генерувати будь-який текстовий формат. тому угода про два розширення спрощує те, як шаблони створюються та відображаються у декількох форматах.

Розташування шаблонів

Шаблони зберігаються за замовчуванням у каталозі templates/. Коли сервіс або контролер відображає шаблон product/index.html.twig, вони насправді посилаються на файл <your-project>/templates/product/index.html.twig.

Каталог шаблонів за замовчуванням можна сконфігурувати за допомогою опції twig.default_path, і ви можете додати більше каталогів шаблонів як пояснюється пізніше у цій статті.

Змінні шаблонів

Розповсюдженою необхідністю шаблонів є виведення значень, які зберігаються у шаблонах, переданих з контролера або сервісу. Змінні зазвичай зберігають об'єкти та масиви, а не рядки, числа та булеві значення. Тому Twig надає швидкий доступ до складних PHP-змінниих. Розгляньте наступний шаблон:

1
<p>{{ user.name }} додав коментар у {{ comment.publishedAt|date }}</p>

Нотація user.name означає, що ви хочете відобразити деяку інформацію (name), що зберігається у змінній (user). user - це масив чи об'єкт? name - це властивість чи метод? У Twig це не має значення.

При використанні нотації foo.bar, Twig намагається отримати значення змінної у наступному порядку:

  1. $foo['bar'] (масив і елемент);
  2. $foo->bar (об'єкт і публічна властивість);
  3. $foo->bar() (об'єкт і публічний метод);
  4. $foo->getBar() (об'єкт і метод getter);
  5. $foo->isBar() (об'єкт і метод isser);
  6. $foo->hasBar() (об'єкт і метод hasser);
  7. Якщо нічого з перерахованого вище не існує, використайте null (або викличте виключення Twig\Error\RuntimeError, якщо увімкнена опція strict_variables).

Це дозволяє розвивати код вашого додатку, не змінюючи код шаблону (ви можете почати зі змінних масиву для апробації концепту додатку, а потім перейти до об'єктів з методами і т.д.)

Посилання на сторінки

Замість написання URL посилань від руки, використайте функцію path(), щоб згенерувати URL, засновуючись на конфігурації маршрутизації .

Пізніше, якщо ви захочете змінити URL конкретної сторінки, все, що вам треба буде зробити - це змінити конфігурацію маршрутизації: шаблони автоматично згенерують новий URL.

Розгляньте наступну конфігурацію маршрутизації:

  • Attributes
  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// src/Controller/BlogController.php
namespace App\Controller;

// ...
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

class BlogController extends AbstractController
{
    #[Route('/', name: 'blog_index')]
    public function index(): Response
    {
        // ...
    }

    #[Route('/article/{slug}', name: 'blog_post')]
    public function show(string $slug): Response
    {
        // ...
    }
}

Використайте функцію Twig path(), щоб послатися на ці сторінки та передати ім'я маршруту в якості першого аргументу, а параметри маршруту в якості необов'язкового другого аргументу:

1
2
3
4
5
6
7
8
9
10
11
<a href="{{ path('blog_index') }}">Homepage</a>

{# ... #}

{% лоя посту у blog_posts %}
    <h1>
        <a href="{{ path('blog_post', {slug: post.slug}) }}">{{ post.title }}</a>
    </h1>

    <p>{{ post.excerpt }}</p>
{% endfor %}

Функція path() генерує відносні URL. Якщо вам потрібно згенерувати абсолютні URL (наприклад, при відображенні шаблонів для електронної пошти або стрічок RSS), використайте функцію url(), яка бере ті ж аргументи, що і path() (наприклад, <a href="{{ url('blog_index') }}"> ... </a>).

Посилання на ресурси CSS, JavaScript та зображень

Якщо шаблону потрібно послатися на статичний ресурс (наприклад, зображення), Symfony надає функцію Twig asset(), щоб допомогти згенерувати цей URL. Спочатку встановіть пакет asset:

1
$ composer require symfony/asset

Тепер ви можете використовувати функцію asset():

1
2
3
4
5
6
7
8
{# зображення живе тут "public/images/logo.png" #}
<img src="{{ asset('images/logo.png') }}" alt="Symfony!"/>

{# CSS-файл живе тут "public/css/blog.css" #}
<link href="{{ asset('css/blog.css') }}" rel="stylesheet"/>

{# JS-файл живе тут "public/bundles/acme/js/loader.js" #}
<script src="{{ asset('bundles/acme/js/loader.js') }}"></script>

Головною метою функції asset() є зробити ваш застосунок більш портативним. Якщо ваш застосунок живе у корені вашого хостингу (наприклад, https://example.com), відображений шлях має бути /images/logo.png. Але якщо ваш застосунок живе у підкаталозі (наприклад, https://example.com/my_app), кожний шлях має відображатися з підкаталогом (наприклад, /my_app/images/logo.png). Функція asset() піклується про це, визначаючи те, як ваш застосунок використовується, і відповідно генеруючи правильні шляхи.

Tip

Функція asset() підтримує різні техніки посилення кешу через опції конфігурації version , version_format , та json_manifest_path .

Якщо вам для ресурсів потрібні абсолютні URL, використайте функцію Twwig absolute_url() наступним чином:

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

<link rel="shortcut icon" href="{{ absolute_url('favicon.png') }}">

Побудова, версіонування та більш просунутий CSS, обробка JavaScript та зображень

Для допомоги з побудовою, версіонуванням та зменшенням ваших ресурсів JavaScript та CSS сучаснним чином, прочитайте про Symfony Webpack Encore.

Глобальна змінна для всього додатку

Symfony створює об'єкт контексту, який автоматично впроваджується у кожний шаблон Twig у вигляді змінної під назвою app. Вона надає доступ до деякої інформації додатку:

1
2
3
4
5
<p>Username: {{ app.user.username ?? 'Anonymous user' }}</p>
{% if app.debug %}
    <p>Request method: {{ app.request.method }}</p>
    <p>Application Environment: {{ app.environment }}</p>
{% endif %}

Змінна app (яка є екземпляром AppVariable) надає вам доступ до таких змінних:

app.user
Поточний об'єкт користувача або null, якщо користувач не аутентифікований.
app.request
Об'єкт Request, який зберігає поточні дані запиту data (в залежності від вашого додатку, це може бути підзапитом або звичайним запитом).
app.session
Об'єкт Session, який надає поточну сесію користувача або null, якщо її немає.
app.flashes
Масив усіх флеш-повідомлень , що зберігаються у сесії. Ви можете також отримати лише повідомлення певного типу (наприклад, app.flashes('notice')).
app.environment
Ім'я поточного середовища конфігурації (dev, prod, і т.д.).
app.debug
True, якщо у режимі налагодження . False - в інших режимах.
app.token
Об'єкт TokenInterface, що надає токен безпеки.
app.current_route
Імʼя маршруту, асоційованого з поточним запитом або null, якщо жодний запит не доступний (еквівалентно app.request.attributes.get('_route'))
app.current_route_parameters
Масив з параметрами, переданими маршруту для поточного запиту, або порожній масив, якщо жодний запит не доступний (еквівалентно app.request.attributes.get('_route_params'))

6.2

Змінні app.current_route та app.current_route_parameters були представлені в Symfony 6.2.

На додаток до глобальної змінної app, впровадженої Symfony, ви також можете автоматично впроваджувати змінні у всі шаблони Twig.

Компоненти Twig

Компоненти Twig - це альтернативний спосіб відображення шаблонів, де кожний шаблон привʼязано до "класу компонента". Це робить простішим відображення та повторне виокристання маленьких "одиниць" шаблонів - таких як сповіщення, розмітка для модального вікна або бічної панелі категорій.

Щоб дізнатися більше, див. Компонент UX Twig.

Компоненти Twig також мають ще одну суперсилу: вони можуть ставати "живими", коли вони автоматично оновлюються (через Ajax) під час взаємодії користувача з ними. Наприклад, коли користувач друкує у полі, ваш компонент Twig повторно відобразиться через Ajax, щоб відобразити список результатів!

Щоб дізнатися більше, див. Компонент UX Live.

Відображення шаблонів

Відображення шаблону в контролерах

Якщо ваш контролер розширюється з AbstractController , використайте помічника render():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// src/Controller/ProductController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ProductController extends AbstractController
{
    public function index(): Response
    {
        // ...

        // метод `render()` повертає об'єкт `Response` зі змістом,
        // створеним шаблоном
        return $this->render('product/index.html.twig', [
            'category' => '...',
            'promotions' => ['...', '...'],
        ]);

        // метод `renderView()` повертає лише зміст, створений шаблоном,
        // тому ви можете використати цей зміст пізніше в об'єкті `Response`
        $contents = $this->renderView('product/index.html.twig', [
            'category' => '...',
            'promotions' => ['...', '...'],
        ]);

        return new Response($contents);
    }
}

Якщо ваш контролер не розширюється з AbstractController, вам буде необхідно отримати сервіси у вашому контролері та використати метод render() сервісу twig.

Ще однією опцією є використання атрибута #[Template()] у методі контролера, щоб визначити шаблон для відображення:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// src/Controller/ProductController.php
namespace App\Controller;

use Symfony\Bridge\Twig\Attribute\Template;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

class ProductController extends AbstractController
{
    #[Template('product/index.html.twig')]
    public function index()
    {
        // ...

        // при використанні атрибута #[Template()], вам потрібно лише повернути масив
        // з параметрами для передачі шаблону (саме атрибут створить та поверне обʼєкт
        // Response).
        return [
            'category' => '...',
            'promotions' => ['...', '...'],
        ];
    }
}

6.2

Атрибут #[Template()] було представлено в Symfony 6.2.

Відображення шаблону у сервісах

Впровадьте сервіс Symfony twig у ваші власні сервіси та використайте його метод render(). При використанні автомонтування сервісів, вам знадобиться лише додати аргумент у конструктор сервісу та додату підказку до класу Environment:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// src/Service/SomeService.php
namespace App\Service;

use Twig\Environment;

class SomeService
{
    private $twig;

    public function __construct(Environment $twig)
    {
        $this->twig = $twig;
    }

    public function someMethod()
    {
        // ...

        $htmlContents = $this->twig->render('product/index.html.twig', [
            'category' => '...',
            'promotions' => ['...', '...'],
        ]);
    }
}

Відображення шаблону в електронних листах

Прочитайте документи про поштову програму та інтеграцію з Twig .

Відображення шаблону прямо з маршруту

Хоча шаблони зазвичай відображаються у контролерах та сервісах, ви можете відобразити статичні сторінки, які не вимагають змінних, прямо з визначення маршруту. Використайте спеціальний TemplateController, наданий Symfony:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# config/routes.yaml
acme_privacy:
    path:          /privacy
    controller:    Symfony\Bundle\FrameworkBundle\Controller\TemplateController
    defaults:
        # шлях шаблону для відображення
        template:  'static/privacy.html.twig'

        # статус-код відповіді (за замовчуванням: 200)
        statusCode: 200

        # спеціальні опції, визначені Symfony для установки кешу сторінки
        maxAge:    86400
        sharedAge: 86400

        # чи має кешування застосовуватися лише до кешів клієнтів
        private: true

        # за бажанням ви можете визначити деякі аргументи, передані шаблону
        context:
            site_name: 'ACME'
            theme: 'dark'

Перевірка існування шаблону

Шаблони завантажуються у застосунок з використанням завантажувача шаблонів Twig, який також надає метод для перевірки існування шаблонів. Спочатку отримайте завантажувач:

1
2
3
4
5
6
7
8
9
10
11
use Twig\Environment;

class YourService
{
    // це код припускає, що ваш сервіс використовує автомонтування для впровадження залежностей
    // у іншому випадку, вручну впровадьте сервіс під назвою 'twig'
    public function __construct(Environment $twig)
    {
        $loader = $twig->getLoader();
    }
}

Потім, передайте шлях шаблону Twig методу завантажувача exists():

1
2
3
4
if ($loader->exists('theme/layout_responsive.html.twig')) {
    // шаблон існує, зробіть щось
    // ...
}

Налагодження шаблонів

Symfony надає декілька утиліть для допомоги з налагодженням проблем у ваших шаблонах.

Перевірка дотримання стандартів кодування шаблонів Twig

Команда lint:twig перевіряє, щоб у ваших шаблонах Twig не було ніяки синтаксичних помилок. Корисно виконувати її до запуску вашого додатку у виробництво (наприклад, на вашому сервері безперервної інтеграції):

1
2
3
4
5
6
7
8
9
# перевірити всі шаблони додатку
$ php bin/console lint:twig

# ви також можете перевірити каталоги та окремі шаблони
$ php bin/console lint:twig templates/email/
$ php bin/console lint:twig templates/article/recent_list.html.twig

# ви також можете побачити застарілі функції, використовувані у ваших шаблонах
$ php bin/console lint:twig --show-deprecations templates/email/

При запуску лінтера всередині дій GitHub, виведення автоматично адаптується до формату, який вимагається GitHub, але ви можете також форсувати цей формат:

1
$ php bin/console lint:twig --format=github

Дослідження інформації Twig

Команда debug:twig перераховує всю доступну інфорамцію про Twig (функції, фільтри, глобальні змінні і т.д.). Вона корисна для перевірки того, чи правильно працюють ваші користувацькі розширення Twig, та перевірки функцій Twig, доданих при установці пакетів :

1
2
3
4
5
6
7
8
# перерахувати загальну інформацію
$ php bin/console debug:twig

# відфільтрувати виведення за будь-яким ключовим словом
$ php bin/console debug:twig --filter=date

# передати шлях шаблону, щоб вказати фізичний файл, який буде завантажено
$ php bin/console debug:twig @Twig/Exception/error.html.twig

Утиліти скидання Twig

Symfony надає функцію dump() в якості покращеної альтернативи PHP-функції var_dump(). Ця функція корисна для дослідження змісту будь-якої змінної, і ви можете використовувати її у шаблонах Twig.

Спочатку переконайтеся, що компонент VarDumper встановлений у додатку:

1
$ composer require symfony/var-dumper

Потім, використайте або тег {% dump %}, або функцію {{ dump() }}, в залежності від ваших потреб:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{# templates/article/recent_list.html.twig #}
{# зміст цієї змінної відправляється Панелі інструментів веб налагодження, а
   не скидається у зміст сторінки #}
{% dump articles %}

{% for article in articles %}
    {# зміст цієї змінної скидається у зміст сторінки,
       і видимий на веб-сторінці #}
    {{ dump(article) }}

    <a href="/article/{{ article.slug }}">
        {{ article.title }}
    </a>
{% endfor %}

Щоб уникнути витоку конфіденційної інформації, функція/тег dump() доступна лише у середовищах конфігурації dev і test. Якщо ви спробуєте використати її у середовищі prod, то побачите PHP-помилку.

Повторне використання змісту шаблонів

Додавання шаблонів

Якщо визначений код Twig повторюється у декількох шаблонах, ви можете отримати його в один "фрагмент шаблону" і додати його у інші шаблони. Уявіть, що наступний код для відображення інформації користувача повторюється у декількох місцях:

1
2
3
4
5
6
7
{# templates/blog/index.html.twig #}

{# ... #}
<div class="user-profile">
    <img src="{{ user.profileImageUrl }}" alt="{{ user.fullName }}"/>
    <p>{{ user.fullName }} - {{ user.email }}</p>
</div>

Спочатку створіть новий шаблон Twig під назвою blog/_user_profile.html.twig (префікс _ необов'язковий, але це угода, використовувана для кращої диференціації між повними шаблонами та їхніми фрагментами).

Потім, видаліть цей зміст з початкового шаблону blog/index.html.twig, і додайте наступне, щоб додати фрагмент шаблону:

1
2
3
4
{# templates/blog/index.html.twig #}

{# ... #}
{{ include('blog/_user_profile.html.twig') }}

Функція Twig include() бере аргумент шляху шаблону, щоб додати його. Доданий шаблон має доступ до всіх змінних шаблону, який його містить (використовуйте опцію with_context, щоб контролювати це).

Ви також можете передати змінні доданому шаблону. Це корисно, наприклад, для перейменування змінних. Уявіть, що ваш шаблон зберігає інформацію користувача у змінній під назвою blog_post.author, а не змінній user, яку очікує фрагмент шаблону. Використайте наступне, щоб перейменувати змінну:

1
2
3
4
{# templates/blog/index.html.twig #}

{# ... #}
{{ include('blog/_user_profile.html.twig', {user: blog_post.author}) }}

Вбудовування контролерів

Додавання фрагментів шаблонів корисно для повторного використанні одного і того ж змісту на декількох сторінках. Однак, ця техніка - не найкраще рішення у деяких випадках.

Уявіть, що фрагмент шаблону відображає три найсвіжіших статті блогу. Щоб зробити це, йому потрібно зробити запит до бази даних, щоб отримати ці статті. При використанні функції include(), вам знадобиться робити один і той же запит до бази даних на кожній сторінці з цим фрагментом. Це не дуже зручно.

Кращою алтернативою буде вбудувати результат виконання деякого контролера з функціями Twig render() і controller().

Спочатку створіть контролер, який відображає визначене число недавніх статей:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/Controller/BlogController.php
namespace App\Controller;

use Symfony\Component\HttpFoundation\Response;
// ...

class BlogController extends AbstractController
{
    public function recentArticles(int $max = 3): Response
    {
        // якось отримати найсвіжіші статті (наприклад, зробити запит до бази даних)
        $articles = ['...', '...', '...'];

        return $this->render('blog/_recent_articles.html.twig', [
            'articles' => $articles
        ]);
    }
}

Потім створіть фрагмент шаблону blog/_recent_articles.html.twig (префікс _ у назві шаблону необов'язковий, але це угода, використовувана для кращої диференціації між повними шаблонами та їхніми фрагментами):

1
2
3
4
5
6
{# templates/blog/_recent_articles.html.twig #}
{% for article in articles %}
    <a href="{{ path('blog_show', {slug: article.slug}) }}">
        {{ article.title }}
    </a>
{% endfor %}

Тепер ви можете викликати цей контролер з будь-якого шаблону, щоб вбудувати його результат:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{# templates/base.html.twig #}

{# ... #}
<div id="sidebar">
    {# якщо контролер асоційований з маршрутом, використайте функції path() або url() #}
    {{ render(path('latest_articles', {max: 3})) }}
    {{ render(url('latest_articles', {max: 3})) }}

    {# якщо ви не хочете розкривати контролер публічним URL, використовуйте
       функцію controller(), щоб визначити контролер для виконання #}
    {{ render(controller(
        'App\\Controller\\BlogController::recentArticles', {max: 3}
    )) }}
</div>

При використанні функції controller(), контролери не доступні з використанням звичайного маршруту Symfony, але доступні через спеціальний URL, використовуваний виключно для обслуговування цих фрагментів шаблону. Сконфігуруйте цей спеціальний URL в опції fragments:

  • YAML
  • XML
  • PHP
1
2
3
4
# config/packages/framework.yaml
framework:
    # ...
    fragments: { path: /_fragment }

Caution

Вбудовування контролерів вимагає відправки запитів цим контролерам та відображення деяких шаблонів у якості результату. Це може мати значний вплив на продуктивність додатку, якщо ви вбудовуєте багато контролерів. Якщо можливо, кешуйте фрагмент шаблону.

See also

Шаблони також можуть вбудовувати зміст асинхронно за допомогою бібліотеки JavaScript hinclude.js.

Наслідування шаблонів та макети

У міру зростання вашого додатку, ви будете знаходити все більше повторюваних елементів між сторінками, таких як заголовки, нижні колонтитули, бічні панелі і т.д. Додавання шаблонів і вбудовування контролероів може допомогти, але коли сторінки мають спільну структуру, краще використовувати наслідування.

Концепт наслідування шаблонів Twig схожий на наслудвання PHP-класів. Ви визначаєте батьківський шаблон, з якого можуть розширюватися інші шаблони, а дочірні шаблони можуть перевизначати частини батьківського.

Symfony рекомендує наступне трьохрівневе наслідування шаблонів для середніх та складних додатків:

  • templates/base.html.twig, визначає спільні елементи всіх шаболнів додатку, такі як <head>, <header>, <footer>, і т.д..;
  • templates/layout.html.twig, розширюється з base.html.twig та визначає структуру змісту, використовувану на (практично) всіх сторінках, таку як зміст у двох колонках + макет бічної панелі. Деякі розділи додатку можуть визначать свої власні макети (наприклад, templates/blog/layout.html.twig);
  • templates/*.html.twig, сторінки додатку, що розширюються з головного шаблону layout.html.twig або будь-кого іншого макета розділу.

На практиці, шаблон base.html.twig виглядатиме так:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}My Application{% endblock %}</title>
        {% block stylesheets %}
            <link rel="stylesheet" type="text/css" href="/css/base.css"/>
        {% endblock %}
    </head>
    <body>
        {% block body %}
            <div id="sidebar">
                {% block sidebar %}
                    <ul>
                        <li><a href="{{ path('homepage') }}">Home</a></li>
                        <li><a href="{{ path('blog_index') }}">Blog</a></li>
                    </ul>
                {% endblock %}
            </div>

            <div id="content">
                {% block content %}{% endblock %}
            </div>
        {% endblock %}
    </body>
</html>

Тег блоків Twig визначає розділи сторінки, які можна перевизначити у дочірніх шаблонах. Вони можуть бути порожніми, як блок content, або визначати зміст за замовчуванням, як блок title, який відображається, коли дочірній шаблон їх не перевизначає.

Шаблон blog/layout.html.twig може бути таким:

1
2
3
4
5
6
7
8
{# templates/blog/layout.html.twig #}
{% extends 'base.html.twig' %}

{% block content %}
    <h1>Blog</h1>

    {% block page_contents %}{% endblock %}
{% endblock %}

Шаблон розширюється з base.html.twig та визначає лише зміст блоку content. Решта блоків батьківського шаблону будуть відображати свій зміст за замовчуванням. Однак, вони можуть бути перевизначені наслідуванням шаблонів третього рівня, таким як blog/index.html.twig, який відображає індекс блогу:

1
2
3
4
5
6
7
8
9
10
11
{# templates/blog/index.html.twig #}
{% extends 'blog/layout.html.twig' %}

{% block title %}Blog Index{% endblock %}

{% block page_contents %}
    {% for article in articles %}
        <h2>{{ article.title }}</h2>
        <p>{{ article.body }}</p>
    {% endfor %}
{% endblock %}

Цей шаблон розширюється з шаблону другого рівня (blog/layout.html.twig), але перевизначає блоки різних батьківських шаблонів: page_contents з blog/layout.html.twig і title з base.html.twig.

Коли ви відображаєте шаблон blog/index.html.twig, Symfony використовує три різних шаблони для створення фінального змісту. Цей механізм наслідування посилює вашу продуктивність, тому що кожний шаблон містить лише свій унікальний зміст, і залишає повторюваний зміст та HTML-структуру якимось батьківським шаблонам.

Caution

При використанні extends, дочірньому шаблону заборонено визначати частини шаблону поза блоком. Наступний код викликає помилку SyntaxError:

1
2
3
4
5
6
7
8
{# app/Resources/views/blog/index.html.twig #}
{% extends 'base.html.twig' %}

{# рядок нижче не зафіксований тегом "block" #}
<div class="alert">Some Alert</div>

{# наступне є валідним #}
{% block content %}My cool blog posts{% endblock %}

Прочитайте документи наслідування шаблонів Twig, щоб дізнаится більше про те, як при перевизначенні використовувати зміст батьківських блоків повторно, та про інші просунуті функції.

Екранування виведення

Уявіть, що ваш шаблон містить код Hello {{ name }} для відображення імені користувача. Якщо зловмисний користувач встановить <script>alert('hello!')</script> в якості свого імені, і ви виведете це значення без змін, застосунок відобразить спливаюче вікно JavaScript.

Це відомо як атака міжсайтового скриптингу (XSS). І хоча попередній приклад виглядає невинним, атакуючий може написати більш просунутий код JavaScript, щоб виконати зловмисні дії.

Щоб запобігти цій атаці, використовуйте "екранування виведення", щоб перетворити символи, які мають особливе значення (наприклад, замініть < на HTML-сутність &lt;). Додатки Symfony безпечні за замовчуванням, так як вони виконують автоматичне екранування виведення завдяки опції автоекранування Twig:

1
2
3
<p>Hello {{ name }}</p>
{# якщо 'name' - '<script>alert('hello!')</script>', Twig виведе це:
   '<p>Hello &lt;script&gt;alert(&#39;hello!&#39;)&lt;/script&gt;</p>' #}

Якщо ви відображаєте змінну, якій можна довіряти, і яка має HTML-зміст, використовуйте фільтр Twig raw, щоб відключити екранування виведення для цієї змінної:

1
2
3
<h1>{{ product.title|raw }}</h1>
{# якщо 'product.title' - 'Lorem <strong>Ipsum</strong>', Twig виведе
   саме це замість 'Lorem &lt;strong&gt;Ipsum&lt;/strong&gt;' #}

Прочитайте документацію екранування виведення Twig, щоб дізнатися більше про те, як відключити екранування виведення для блоку або навіть всього шаблону.

Простори імен шаблонів

Хоча більшість додатків зберігає свої шаблони у каталозі за замовчуванням templates/, вам може знадобитися зберігати деякі (або всі) з них у інших каталогах. Використайте опцію twig.paths, щоб сконфігурувати ці додаткові каталоги. Кожний шлях визначається як пара key: value, де key - це каталог шаблону, а value - простір імен Twig, що пояснюється пізніше:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
7
8
# config/packages/twig.yaml
twig:
    # ...
    paths:
        # каталоги відносні до кореневого каталогу проекту (але ви також
        # можете використовувати абсолютні каталоги)
        'email/default/templates': ~
        'backend/templates': ~

При відображенні шаблону, Symfony спочатку шукає його у каталогах twig.paths, які не визначають простір імен, а потім повертається до каталогу шаблонів за замовчуванням (зазвичай, templates/).

Використовуючи конфігурацію вище, якщо ваш застосунок відображає, наприклад, шаблон layout.html.twig, Symfony спочатку шукатиме у email/default/templates/layout.html.twig і backend/templates/layout.html.twig. Якщо будь-який з цих шаблонів існує, Symfony використає його замість використання templates/layout.html.twig, щоб скоріш за все є тим шаблоном, який ви хотіли використати.

Twig вирішує цю проблему за допомогою просторів імен, які групують декілька шаблонів під одним логічним ім'ям, не пов'язаним з їх реальним розташуванням. Оновіть попередню конфігурацію, щоб визначити простір імен для кожного каталогу шаблонів:

  • YAML
  • XML
  • PHP
1
2
3
4
5
6
# config/packages/twig.yaml
twig:
    # ...
    paths:
        'email/default/templates': 'email'
        'backend/templates': 'admin'

Тепер, якщо ви відобразите шаблон layout.html.twig, Symfony відображатиме файл templates/layout.html.twig. Використайте спеціальний синтаксис @ + простір імен, щоб посилатися на інші шаблони з просторами імен (наприклад, @email/layout.html.twig і @admin/layout.html.twig).

Note

Один простір імен Twig може асоціюватися більш ніж з одним каталогом шаблонів. У такому випадку, порядк, в якому додаються шляхи, важливий, так як Twig почне шукати шаблони з першого визначеного шляху.

Шаблони пакетів

Якщо ви встановлюєте пакети у своєму додатку, вони можуть містити власні шаблони Twig (у каталозі Resources/views/ кожного пакета). Щоб уникнути плутанини з вашими власними шаблонами, Symfony додає шаблони пакетів під автоматичним простором імен, створеним за ім'ям пакета.

Наприклад, шаблони під назвою AcmeFooBundle доступні під простором імен AcmeFoo. Якщо цей пакет включає у себе шаблон <your-project>/vendor/acmefoo-bundle/Resources/views/user/profile.html.twig, ви можете послатися на нього так @AcmeFoo/user/profile.html.twig.

Tip

Ви також можете перевизначати шаблони пакетів у випадку, якщо ви хочете змінити деякі частини початкових шаблонів пакета.