Наследование шаблонов
Twig предоставляет мощные механизмы для организации и переиспользования шаблонов.
Основные шаблоны и переопределения
Основные шаблоны поставляются вместе с MMO-DEV WEB и доступны только для чтения на рабочем сайте. Чтобы изменить шаблон сайта или личного кабинета, откройте его в панели управления, выберите стиль и создайте переопределение. Изменения сохраняются только в выбранном стиле и не изменяют исходный шаблон продукта.
После обновления MMO-DEV WEB исходный шаблон может измениться, а ваше переопределение сохранится. Совместимые изменения объединяются автоматически. Если одни и те же строки были изменены и в продукте, и в переопределении, панель управления покажет конфликт для сравнения и ручного объединения. Административные шаблоны изменяются только при разработке продукта.
Наследование (extends)
Наследование позволяет создать базовый макет и переопределять его части в дочерних шаблонах.
Базовый макет
{# @site/layout.html.twig #}
<!DOCTYPE html>
<html lang="{{ mw.lang }}">
<head>
<meta charset="UTF-8">
<title>{% block title %}{{ mw.projectName }}{% endblock %}</title>
{% block stylesheets %}
<link rel="stylesheet" href="{{ asset('css/style.css') }}">
{% endblock %}
</head>
<body>
{% block header %}
{% include '@site/partials/header.html.twig' %}
{% endblock %}
<main>
{% block content %}{% endblock %}
</main>
{% block footer %}
{% include '@site/partials/footer.html.twig' %}
{% endblock %}
{% block javascripts %}
<script src="{{ asset('js/app.js') }}"></script>
{% endblock %}
</body>
</html>
Дочерний шаблон
{# @site/pages/news.html.twig #}
{% extends '@site/layout.html.twig' %}
{% block title %}Новости - {{ parent() }}{% endblock %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{ asset('css/news.css') }}">
{% endblock %}
{% block content %}
<h1>Новости</h1>
{% for article in news %}
<article>
<h2>{{ article.title }}</h2>
<p>{{ article.summary }}</p>
</article>
{% endfor %}
{% endblock %}
parent()
Функция parent() возвращает содержимое блока из родительского шаблона:
{% block sidebar %}
{{ parent() }}
{# Добавляем контент к родительскому, а не заменяем #}
<div class="extra-widget">...</div>
{% endblock %}
Тег {% extends %} должен быть первым тегом в шаблоне. Любой текст до него вызовет ошибку.
Включение шаблонов (include)
Базовое включение
{% include '@common/components/alert.html.twig' %}
{# С передачей дополнительных переменных #}
{% include '@common/components/card.html.twig' with {
title: 'Заголовок',
body: 'Содержимое'
} %}
{# Только указанные переменные (без доступа к контексту родителя) #}
{% include '@common/components/card.html.twig' with {
title: 'Заголовок'
} only %}
Условное включение
{# Игнорировать ошибку, если шаблон не найден #}
{% include '@site/partials/sidebar.html.twig' ignore missing %}
{# Попробовать несколько шаблонов #}
{% include [
'@site/pages/' ~ page ~ '.html.twig',
'@site/pages/default.html.twig'
] %}
Встроенное включение (embed)
embed - комбинация include и extends. Позволяет включить шаблон и переопределить его блоки:
{# Компонент @common/components/modal.html.twig #}
<div class="modal">
<div class="modal-header">
{% block header %}Заголовок{% endblock %}
</div>
<div class="modal-body">
{% block body %}{% endblock %}
</div>
<div class="modal-footer">
{% block footer %}
<button class="btn">Закрыть</button>
{% endblock %}
</div>
</div>
{# Использование embed #}
{% embed '@common/components/modal.html.twig' %}
{% block header %}Подтверждение удаления{% endblock %}
{% block body %}
<p>Вы уверены, что хотите удалить этот элемент?</p>
{% endblock %}
{% block footer %}
<button class="btn btn-danger">Удалить</button>
<button class="btn">Отмена</button>
{% endblock %}
{% endembed %}
Горизонтальное переиспользование (use)
use импортирует блоки из другого шаблона (аналог traits в PHP):
{# blocks/forms.html.twig #}
{% block input %}
<input type="text" name="{{ name }}" value="{{ value }}">
{% endblock %}
{% block textarea %}
<textarea name="{{ name }}">{{ value }}</textarea>
{% endblock %}
{# Импорт блоков #}
{% use 'blocks/forms.html.twig' %}
{# Теперь блоки input и textarea доступны #}
{{ block('input') }}
{# Переименование при конфликте имён #}
{% use 'blocks/forms.html.twig' with input as form_input %}
Макросы (macro)
Макросы - переиспользуемые фрагменты шаблона, аналог функций:
Определение
{# @common/macros/forms.html.twig #}
{% macro input(name, value, type, attrs) %}
{% set type = type|default('text') %}
{% set attrs = attrs|default({}) %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}"
{% for attr, val in attrs %} {{ attr }}="{{ val }}"{% endfor %}
>
{% endmacro %}
{% macro select(name, options, selected) %}
<select name="{{ name }}">
{% for value, label in options %}
<option value="{{ value }}"
{% if value == selected %} selected{% endif %}
>{{ label }}</option>
{% endfor %}
</select>
{% endmacro %}
Импорт и использование
{# Импорт всех макросов #}
{% import '@common/macros/forms.html.twig' as forms %}
{{ forms.input('email', '', 'email', {class: 'form-control', required: 'required'}) }}
{{ forms.select('country', {ru: 'Россия', us: 'США'}, 'ru') }}
{# Импорт конкретных макросов #}
{% from '@common/macros/forms.html.twig' import input, select %}
{{ input('username', user.name) }}
{{ select('role', roles, user.role) }}
Макросы в том же файле
{# Определение и использование в одном шаблоне #}
{% macro badge(text, type) %}
<span class="badge badge-{{ type|default('info') }}">{{ text }}</span>
{% endmacro %}
{# Вызов через _self (устаревший способ) или import #}
{% import _self as self %}
{{ self.badge('Новинка', 'success') }}
Сводная таблица
| Механизм | Назначение | Переопределение блоков |
|---|---|---|
extends | Наследование макета | Да |
include | Вставка шаблона | Нет |
embed | Вставка с переопределением | Да |
use | Импорт блоков (traits) | Нет (переименование) |
macro | Переиспользуемые функции | Нет |