Twig автоматически устанавливается с Symfony, но если нужно добавить отдельно:
composer require twig
Конфигурация по умолчанию (config/packages/twig.yaml
):
twig:
default_path: '%kernel.project_dir%/templates'
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
form_themes: ['bootstrap_5_layout.html.twig']
Рекомендуемая структура папок:
templates/
├── base.html.twig # Базовый шаблон
├── components/ # Компоненты
├── pages/ # Страницы
│ ├── blog/
│ │ ├── index.html.twig
│ │ └── show.html.twig
└── macros/ # Макросы
Пример base.html.twig
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}My App{% endblock %}</title>
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
</head>
<body>
{% include 'components/header.html.twig' %}
<main class="container">
{% block body %}{% endblock %}
</main>
{% include 'components/footer.html.twig' %}
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</body>
</html>
Пример страницы, расширяющей базовый шаблон:
{% extends 'base.html.twig' %}
{% block title %}Blog Posts{% endblock %}
{% block body %}
<h1>Latest Posts</h1>
{% for post in posts %}
<article>
<h2>{{ post.title }}</h2>
<p>{{ post.content|u.truncate(200) }}...</p>
</article>
{% endfor %}
{% endblock %}
Из контроллера:
#[Route('/blog', name: 'blog_index')]
public function index(): Response
{
$posts = $this->getDoctrine()
->getRepository(Post::class)
->findAll();
return $this->render('pages/blog/index.html.twig', [
'posts' => $posts,
'page_title' => 'Blog Archive'
]);
}
{{ post.createdAt|date('d.m.Y H:i') }}
{{ user.description|striptags|upper }}
{{ price|format_currency('USD') }}
{{ path('app_blog_show', {id: post.id}) }}
{{ asset('images/logo.png') }}
{{ csrf_token('authenticate') }}
{% macro input(name, value, type = 'text') %}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{% endmacro %}
{{ _self.input('username') }}
Пример компонента templates/components/alert.html.twig
:
<div class="alert alert-{{ type }}">
{{ message }}
{% if dismissible %}
<button class="close">×</button>
{% endif %}
</div>
Использование:
{% include 'components/alert.html.twig' with {
type: 'success',
message: 'Operation completed!',
dismissible: true
} %}
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFilter;
class AppExtension extends AbstractExtension
{
public function getFilters()
{
return [
new TwigFilter('excerpt', [$this, 'createExcerpt']),
];
}
public function createExcerpt(string $text, int $length = 50): string
{
if (mb_strlen($text) <= $length) {
return $text;
}
return mb_substr($text, 0, $length) . '...';
}
}
{{ post.content|excerpt(100) }}
Рендеринг формы:
{{ form_start(post_form) }}
{{ form_errors(post_form) }}
{{ form_row(post_form.title) }}
{{ form_row(post_form.content) }}
<button type="submit">Save</button>
{{ form_end(post_form) }}
composer require --dev twig/test
Пример теста:
use Twig\Test\IntegrationTestCase;
class TwigTest extends IntegrationTestCase
{
public function getFixturesDir()
{
return __DIR__.'/Fixtures/';
}
}
Twig — это современный, безопасный шаблонизатор для Symfony
Ключевые возможности:
Best Practices:
{{ }}
делает это автоматически)Производительность:
Для сложных проектов: