Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Twig templates

Twig is Symfony's template language. It has no PHP code: only markup and short constructs for printing data. Templates live in the templates folder and are named name.html.twig.

Printing

Double curly braces print a value:

<h1>{{ title }}</h1>
<p>Author: {{ post.author.name }}</p>

The dot works for arrays and objects alike: post.title calls the getTitle() method or reads the title key.

All output is escaped automatically: a <script> from user data turns into harmless text.

Conditions

{% if posts is empty %}
    <p>No posts yet.</p>
{% elseif posts|length == 1 %}
    <p>One post.</p>
{% else %}
    <p>Posts: {{ posts|length }}</p>
{% endif %}

Constructs that do something are written in {% %}, output in {{ }}.

Loops

<ul>
{% for post in posts %}
    <li>{{ loop.index }}. {{ post.title }}</li>
{% else %}
    <li>Nothing found</li>
{% endfor %}
</ul>

The else branch runs when the list is empty. The loop variable holds the step: loop.index, loop.first, loop.last.

Filters

A filter transforms a value and is written after a vertical bar:

{{ post.title|upper }}
{{ post.createdAt|date('Y-m-d') }}
{{ post.body|slice(0, 100) }}…
{{ user.nickname|default('Guest') }}
{{ price|number_format(2, '.', ',') }}

Inheritance

The common page frame is described once — in a base template with blocks:

{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}Blog{% endblock %}</title>
</head>
<body>
    <main>{% block body %}{% endblock %}</main>
</body>
</html>

A page inherits the frame and fills the blocks:

{# templates/blog/index.html.twig #}
{% extends 'base.html.twig' %}
 
{% block title %}Posts{% endblock %}
 
{% block body %}
    <h1>Posts</h1>
{% endblock %}

Including parts

A repeated fragment goes into a separate file:

{% for post in posts %}
    {{ include('blog/_card.html.twig', {post: post}) }}
{% endfor %}

By convention such file names start with an underscore.

<a href="{{ path('blog_show', {slug: post.slug}) }}">Read</a>
<img src="{{ asset('images/logo.png') }}" alt="Logo">

path() builds an address from a route name, asset() a path to a file in the public folder.

Common mistake

The raw filter turns escaping off. If you print user-entered text through it, someone else's script gets onto the page. Apply raw only to markup you created yourself or cleaned on the server.

{{ comment.text|raw }}  {# dangerous #}
{{ comment.text }}      {# safe #}

Thanks for reading!

Donate to the site — we work hard for you.