SlideShare ist ein Scribd-Unternehmen logo
1 von 204
Downloaden Sie, um offline zu lesen
Javier EguiluzSeptember 22, 2015
Twig
TRACK ROOM DATE SPEAKER
Symfony 115
Mastering
License of this presentation
creativecommons.org/licenses/by-nc-sa/3.0
ABOUT ME
Javier Eguiluz
Symfony Evangelist
ABOUT THIS
TALK
We won't talk about
Twig basics.
We won't provide all
the low-level details.
How can
I create a
theme? Which is
the syntax of
Twig?
Read the excellent Twig
documentation to get
those details.
DRUPAL
& TWIG
Fast Easy to learn
Documented Concise Full featured
Extensible Tested Useful errors
Secure
Main Twig features
My favorite feature
Consistent
My favorite feature
Twig defines a very
small set of features…
… which are enough to
create any template
Consistent
same syntax and
behavior since day one!
easy to learn!
«Using Twig templates
is the best decision
Drupal ever made»
DRUPAL 8
TWIG
Built-in Drupal templates
use ~30% of the
available Twig features.
AGENDA
Defensive
programming
White spaces
Debug Escaping
Reusing
templates
Dates
Dynamic
templates
Cool
features
Variables
Agenda
ACCESSING
VARIABLES
WHY IS THIS IMPORTANT??
Because you can easily
improve the performance of
your site/app.
Accessing simple variables
!
<p class="comment__author">{{ author }}</p>
<p class="comment__time">{{ created }}</p>
<p class="comment__permalink">{{ permalink }}</p>
core/themes/bartik/templates/comment.html.twig
Accessing complex variables
!
<nav>{{ content.links }}</nav>
core/themes/bartik/templates/comment.html.twig
Accessing complex variables
!
<nav>{{ content.links }}</nav>
core/themes/bartik/templates/comment.html.twig
This is how Twig resolves complex variables
<nav>{{ content.links }}</nav>
!
$content['links']
$content->links
$content->links()
$content->getLinks()
$content->isLinks()
null
Twig tries all these alternatives
and uses the first one that exists.
And what about
performance?
Resolving variables is quite expensive
<nav>{{ content.links }}</nav>
!
$content['links']
$content->links
$content->links()
$content->getLinks()
$content->isLinks()
null
Resolving a variable is the most
expensive Twig task, specially
for very complex templates.
Improving Twig performance
• Twig provides a PHP extension.
• This extension only implements
the variable resolving logic.
• See twig.sensiolabs.org/doc/
installation.html#installing-the-
c-extension
EXPECTED

PERFORMANCE
INCREASE
15%
Some Drupal variables names are special
!
$variables['site_slogan']['#markup'] = ...
!
{{ site_slogan.#markup }}
core/themes/bartik/bartik.theme
This doesn't work because
of the # character
Some Drupal variables names are special
!
$variables['site_slogan']['#markup'] = ...
!
{{ site_slogan.#markup }}
core/themes/bartik/bartik.theme
This doesn't work because
of the # character
{{ site_slogan['#markup'] }}
{{ attribute(site_slogan, '#markup') }}
DEFENSIVE
PROGRAMMING
WHY IS THIS IMPORTANT??
Because sooner or later
errors will happen. What
matters is how you deal
with them.
Dealing with undefined/empty variables
is empty defaultis defined
is null {% if %}
The two recommended safeguards
{% if variable %}
...
{% endif %}
!
!
Hi {{ variable|default('user') }}
The two recommended safeguards
{% if variable %}
...
{% endif %}
!
!
Hi {{ variable|default('user') }}
It checks that variable is not null
or empty or zero
!
ONLY works if variable is defined
The two recommended safeguards
{% if variable %}
...
{% endif %}
!
!
Hi {{ variable|default('user') }}
It checks that variable is not null
or empty or zero
!
ONLY works if variable is defined
It checks that variable is not null,
empty or undefined
!
It ALWAYS works as expected
Combining both safeguards
{% if variable|default('user') %}
...
{% endif %} It doesn't matter if the variable is not
defined, because the expression will
always have a default value.
Checking that the variable is defined
{% if variable is defined %}
...
{% endif %} A good practice when the rendered
template cannot be sure about the
variables passed from the code.
!
In Drupal 8 this problem should not
happen (the variable list is strict).
Other safeguards available
{% if variable is null %} ... {% endif %}
!
!
{% if variable is empty %} ... {% endif %}
{% if variable is not empty %} ... {% endif %}
Be ready when iterating empty collections
{% for item in collection %}
...
{% else %}
There are no items.
{% endfor %}
Filter values before using them in the loop
{% for item in collection if item.published %}
...
{% else %}
There are no items.
{% endfor %}
Avoid missing templates
{{ include('menu.twig') }}
This will always work because our
theme will provide this template.
Avoid missing templates
{{ include('menu.twig') }}
This will always work because our
theme will provide this template.
Templates with dynamic
paths are very prone to error
{{ include('users/' ~ user.name ~ '/bio.twig') }}
Define fallback templates
{{ include([
'users/' ~ user.name ~ '/bio.twig',
'users/' ~ user.name ~ '/default.twig',
'common/user_bio.twig'
]) }}
Twig includes the first
template that exists
Avoid missing templates
• Sometimes it's not possible to provide fallback
templates.
• Moreover, in some cases, it's better to ignore the
missing template instead of displaying an error to
the user.
Ignore missing templates
{{ include('template.twig', ignore_missing = true) }}
!
{{ source('template.twig', ignore_missing = true) }}
!
{% embed 'template.twig' ignore missing %}
...
{% endembed %}
Ignore missing templates
{{ include('template.twig', ignore_missing = true) }}
!
{{ source('template.twig', ignore_missing = true) }}
!
{% embed 'template.twig' ignore missing %}
...
{% endembed %} NOTE
no underscore here
Twig filters defined by Drupal 8
{{ value|t }}
{{ value|trans }}
{{ value|passthrough }}
{{ value|placeholder }}
{{ value|drupal_escape }}
{{ value|safe_join }}
{{ value|without }}
{{ value|clean_class }}
{{ value|clean_id }}
{{ value|render }}
It's common for a long-
standing and complex project
to add and remove filters.
!
If Drupal removes a filter
used by your templates, your
site/app will break.
Declare filters as deprecated
new Twig_SimpleFilter('old_filter', ..., array(
'deprecated' => true,
'alternative' => 'new_filter'
));
Declare filters as deprecated
new Twig_SimpleFilter('old_filter', ..., array(
'deprecated' => true,
'alternative' => 'new_filter'
));
These deprecations notices are not displayed or logged
anywhere on Drupal yet.
NOTE
Avoid missing blocks
{% if 'title' is block %}
<title>{{ block('title') }}<title>
{% endif %}
This feature is not available yet. It will be included in the
upcoming 1.23 version of Twig.
NOTE
Avoid missing blocks
{% if 'title' is block %}
<title>{{ block('title') }}<title>
{% endif %}
WHITE
SPACES
WHY IS THIS IMPORTANT??
Because it will make your
templates more readable
and it will save you time.
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
The "problem" of white spaces
Twig template HTML page
<ul>
{% for i in 1..3 %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
The "problem" of white spaces
Twig template HTML page
<ul>
{% for i in 1..3 %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
The "problem" of white spaces
Twig template HTML page
<ul>
{% for i in 1..3 %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
The "problem" of white spaces
Twig template HTML page
<ul>
{% for i in 1..3 %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
The "problem" of white spaces
Twig template HTML page
<ul>
{% for i in 1..3 %}
<li>{{ i }}</li>
{% endfor %}
</ul>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
The "problem" of white spaces
Twig template HTML page
Removing white spaces
<ul>
{%- for i in 1..3 -%}
<li>{{ i }}</li>
{%- endfor -%}
</ul>
<ul>
{% spaceless %}
{% for i in 1..3 %}
<li>{{ i }}</li>
{% endfor %}
{% endspaceless %}
</ul>
Please, don't waste
your time dealing with
white spaces.
!
Twig templates
should be readable
HTML pages
should not
!
!
Twig templates
should be readable
HTML pages
should not
this is where you
work everyday
!
browsers get a minimized
and compressed HTML mess
Sometimes you
should add white
spaces…
White spaces around HTML attributes
<h2{{ title_attributes }}>{{ label }}</h2>
core/modules/block/templates/block.html.twig
White spaces around HTML attributes
<h2{{ title_attributes }}>{{ label }}</h2>
core/modules/block/templates/block.html.twig
White spaces around HTML attributes
<h2{{ title_attributes }}>{{ label }}</h2>
core/modules/block/templates/block.html.twig
no white space when
the attributes are empty
<h2 class="..."> ... </h2>
<h2> ... </h2>
Add white spaces to separate Twig & HTML
<h2 {{ title_attributes }}>{{ label }}</h2>
!
!
<h2 class="..."> ... </h2>
<h2 > ... </h2>
Add white spaces to separate Twig & HTML
<h2 {{ title_attributes }}>{{ label }}</h2>
!
!
<h2 class="..."> ... </h2>
<h2 > ... </h2>
white space when the
attributes are empty
Add white spaces to separate Twig & HTML
<h2 {{ title_attributes }}>{{ label }}</h2>
!
!
<h2 class="..."> ... </h2>
<h2 > ... </h2>
white space when the
attributes are empty
IT DOES NOT MATTER
Twig template is
more readable
HTML code with white
spaces is still valid
Hiding HTML code inside Twig strings
<div id="site-name"{{ hide_name ? ' class="hidden"' }}>
!
!
!
!
!
<div id="site-name">
<div id="site-name" class="hidden">
core/themes/bartik/templates/maintenance-page.html.twig
Hiding HTML code inside Twig strings
<div id="site-name"{{ hide_name ? ' class="hidden"' }}>
!
!
!
!
!
<div id="site-name">
<div id="site-name" class="hidden">
core/themes/bartik/templates/maintenance-page.html.twig
Hiding HTML code inside Twig strings
<div id="site-name"{{ hide_name ? ' class="hidden"' }}>
!
!
!
!
!
<div id="site-name">
<div id="site-name" class="hidden">
WARNING
HTML attributes
defined in Twig strings
are easy to overlook
core/themes/bartik/templates/maintenance-page.html.twig
Hiding HTML code inside Twig strings
<div id="site-name"{{ hide_name ? ' class="hidden"' }}>
!
!
!
!
!
<div id="site-name">
<div id="site-name" class="hidden">
WARNING
HTML attributes
defined in Twig strings
are easy to overlook
core/themes/bartik/templates/maintenance-page.html.twig
DANGER
If you miss this single
white space, the page
design breaks
Don't hide HTML code inside Twig strings
<div id="site-name" class="{{ hide_name ? 'hidden' }}">
!
!
!
!
!
<div id="site-name" class="">
<div id="site-name" class="hidden">
HTML & Twig
are decoupled
A single white space
won't break the page
Don't hide HTML code inside Twig strings
<div id="site-name" class="{{ hide_name ? 'hidden' }}">
!
!
!
!
!
<div id="site-name" class="">
<div id="site-name" class="hidden">
Valid HTML code
(tested with the W3C validator)
HTML & Twig
are decoupled
A single white space
won't break the page
DEBUG
WHY IS THIS IMPORTANT??
Because it will save you a
lot of time while developing
your templates.
Configure Twig behavior
# sites/default/services.yml
parameters:
twig.config:
debug: true
auto_reload: null
cache: true
Configure Twig behavior
# sites/default/services.yml
parameters:
twig.config:
debug: true
auto_reload: null
cache: true
Include debug information in
the rendered HTML contents
In production server,
always set it to false
HTML content of a rendered Drupal template
<div id="block-bartik-login" class="contextual-region block block-user block-user-login-block"
role="form">
<h2>User login</h2>
<div data-contextual-id="block:block=bartik_login:langcode=en"></div>
<div class="content">
!
<form class="user-login-form" data-drupal-selector="user-login-form" action="/node?destination=/
node" method="post" id="user-login-form" accept-charset="UTF-8">
!
<!-- ... -->
HTML content when Twig debug is enabled
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
* block--bartik-login.html.twig
* block--user-login-block.html.twig
* block--user.html.twig
x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' -->
<div id="block-bartik-login" class="contextual-region block block-user block-user-login-block"
role="form">
<h2>User login</h2>
<div data-contextual-id="block:block=bartik_login:langcode=en"></div>
<div class="content">
!
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'form' -->
<!-- BEGIN OUTPUT from 'core/themes/classy/templates/form/form.html.twig' -->
<form class="user-login-form" data-drupal-selector="user-login-form" action="/node?destination=/
How to override the current template
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
* block--bartik-login.html.twig
* block--user-login-block.html.twig
* block--user.html.twig
x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' -->
How to override the current template
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
* block--bartik-login.html.twig
* block--user-login-block.html.twig
* block--user.html.twig
x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' -->
Drupal tried to use all these
templates…
How to override the current template
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
* block--bartik-login.html.twig
* block--user-login-block.html.twig
* block--user.html.twig
x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' -->
…before deciding to use this
template.
Drupal tried to use all these
templates…
Which variables are passed to the template?
Built-in templates include
comments with the full
list of variables passed to
the Twig template.
Easier way to introspect all variables
<pre>
{{ dump() }}
</pre>
Easier way to introspect all variables
<pre>
{{ dump() }}
</pre>
It dumps the contents
of all the variables
defined in the template.
It's better to dump just the variables you need
<pre>
{{ dump(label, title_attributes) }}
</pre>
It's better to dump just the variables you need
<pre>
{{ dump(label, title_attributes) }}
</pre>
It dumps only the
given variables
CAUTION!
Don't forget to rebuild your
cache after changing the
config files and templates.
drupalconsole.com
drupalconsole.com
$ drupal cache:rebuild
$ drupal c:r
ESCAPING
WHY IS THIS IMPORTANT??
Because it can prevent you
a lot of security-related
problems.
CAUTION!
Drupal has replaced the
default Twig escaping filter
by their own.
By default, contents are escaped for HTML
Hi {{ content }}!
$content = '<strong>John</strong>';
By default, contents are escaped for HTML
Hi {{ content }}!
$content = '<strong>John</strong>';
What you expect…
Hi John!
What you get…
Hi <strong>John
</strong>!
The "raw" filter prevents the escaping
Hi {{ content|raw }}!
$content = '<strong>John</strong>';
The "raw" filter prevents the escaping
Hi {{ content|raw }}!
$content = '<strong>John</strong>';
What you expect…
Hi John!
What you get…
Hi John!
What if contents are used in URLs or JS?
<a href="...?param={{ value }}"></a>
!
!
!
<script>
var variable = "{{ content }}";
</script>
What if contents are used in URLs or JS?
<a href="...?param={{ value }}"></a>
!
!
!
<script>
var variable = "{{ content }}";
</script>
WRONG HTML
ESCAPING
Applying different escaping strategies
<a href="...?param={{ value|e('url') }}"></a>
!
!
!
<script>
var variable = "{{ content|e('js') }}";
</script>
Escaping strategies available in Twig
{{ content|e('html') }}
{{ content|e('js') }}
{{ content|e('css') }}
{{ content|e('url') }}
{{ content|e('html_attr') }}
REUSING
TEMPLATES
WHY IS THIS IMPORTANT??
Because it allows you to
avoid repeating code and it
makes your themes easier
to maintain.
Lots of different ways to reuse templates
{% embed %} {% extends %}include()
{% set %} {% use %}macro()
How often are these alternatives used
{% embed %}
{% extends %} include( )
{% set %} {% use %}
macro( )
Always
Sometimes
Rarely
Lots of different ways to reuse templates
{% embed %} {% extends %}include()
{% set %} {% use %}macro()
Use {% extends %} to share layouts
Use {% extends %} to share layouts
1 layout with the common design elements
Use {% extends %} to share layouts
1 layout with the common design elements
+
4 simple pages which only define their contents
layout.twig
<!DOCTYPE html>
<html>
<head>
<title>
{% block title %}ACME website{% endblock %}
</title>
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
layout.twig
<!DOCTYPE html>
<html>
<head>
<title>
{% block title %}ACME website{% endblock %}
</title>
</head>
<body>
<div class="container">
{% block content %}{% endblock %}
</div>
</body>
</html>
Other templates can reuse this layout
{% extends 'layout.twig' %}
!
{% block title %}Community{% endblock %}
{% block content %}
<div> ... </div>
{% endblock %}
When should you use {% extends %}
• To create the layout of your theme.
• If your site/app is very complex, create two
inheritance levels (base layout and section layouts).
layout.twig schedule.twig training.twig
extends layout.twig extends schedule.twig
Lots of different ways to reuse templates
{% embed %} {% extends %}include()
{% set %} {% use %}macro()
Reusing templates with include( )
!
{{ include('listing.twig') }}
!
!
<div>
{% for item in items %}
<h2>{{ item.title }}</h2>
<p>{{ item.content }}</p>
{% endfor %}
</div>
blog/index.twig
blog/listing.twig
When should you use include( )
• To reuse large fragments of code, such as sidebars,
navigation menus, etc.
Lots of different ways to reuse templates
{% embed %} {% extends %}include()
{% set %} {% use %}macro()
Repetitive HTML fragments
<div class="form-group">
<label for="{{ id }}">{{ label }}</label>
<input type="{{ type }}" class="form-control"
id="{{ id }}">
</div> Repeating the same HTML
code for all the form fields
is cumbersome
Reusing fragments with macro( )
{% macro form_field(id, label, type="text") %}
<div class="form-group">
<label for="{{ id }}">{{ label }}</label>
<input type="{{ type }}" class="form-control"
id="{{ id }}">
</div>
{% endmacro %}
Using "macros" inside templates
{% import _self as macro %}
!
<form>
{{ macro.form_field('first_name', 'First Name') }}
{{ macro.form_field('last_name', 'Last Name') }}
{{ macro.form_field('email', 'Email', 'email') }}
...
</form>
Using "macros" inside templates
{% import _self as macro %}
!
<form>
{{ macro.form_field('first_name', 'First Name') }}
{{ macro.form_field('last_name', 'Last Name') }}
{{ macro.form_field('email', 'Email', 'email') }}
...
</form>
Before using a macro, you must
"import" them (they can be
defined in a different template)
When should you use macro( )
• To reuse short fragments of code, usually in the
same template (e.g. listings, grids, forms, etc.)
• If your site/app is very complex, store all the macros
in a single file and reuse it from any other template.
Lots of different ways to reuse templates
{% embed %} {% extends %}include()
{% set %} {% use %}macro()
Grid-based design
embed allows to reuse inner page
structures (e.g. the 3-column grid)
Grid-based design
embed allows to reuse inner page
structures (e.g. the 3-column grid)
You can't use "include" to solve this problem
{{ include('common/grid_3.twig') }}
You can't use "include" to solve this problem
{{ include('common/grid_3.twig') }}
you can't change the included
contents (you include both the
structure and the content)
You can't use "extends" to solve this problem
{% extends 'common/grid_3.twig' %}
!
{% block column1 %} ... {% endblock %}
{% block column2 %} ... {% endblock %}
{% block column3 %} ... {% endblock %}
You can't use "extends" to solve this problem
{% extends 'common/grid_3.twig' %}
!
{% block column1 %} ... {% endblock %}
{% block column2 %} ... {% endblock %}
{% block column3 %} ... {% endblock %}
you can't make the whole structure
of the page (grid 2, grid 3, etc.)
because you can't extend from
multiple templates at the same time
Define a three-column grid template
<div class="row">
<div class="col-md-4">
{% block column1 %}{% endblock %}
</div>
!
<div class="col-md-4">
{% block column2 %}{% endblock %}
</div>
!
<div class="col-md-4">
{% block column3 %}{% endblock %}
</div>
</div>
Reuse the three-column grid template
{% embed 'common/grid_3.twig' %}
{% block column1 %}
... contents ...
{% endblock %}
!
{% block column2 %}
... contents ...
{% endblock %}
!
{% block column3 %}
... contents ...
{% endblock %}
{% endembed %}
When should you use {% embed %}
• To reuse page structures across different templates
(e.g. grids)
Lots of different ways to reuse templates
{% embed %} {% extends %}include()
{% set %} {% use %}macro()
Reusing fragments with {% set %}
{% set navigation %}
<a href="...">Previous</a>
...
...
<a href="...">Next</a>
{% endset %}
Reusing fragments with {% set %}
{% set navigation %}
<a href="...">Previous</a>
...
...
<a href="...">Next</a>
{% endset %} {{ navigation }}
{{ navigation }}
When should you use {% set %}
• To reuse short fragments of code inside a template
(if those fragments are configurable, use a macro).
• It's like an internal include() made from inside the
template itself.
Lots of different ways to reuse templates
{% embed %} {% extends %}include()
{% set %} {% use %}macro()
When should you use {% use %}
• This is too advanced and for very specific use
cases.
• You should probably never use it when creating
themes.
DYNAMIC
TEMPLATES
WHY IS THIS IMPORTANT??
Because Drupal allows to
create sites with very
advanced needs.
Templates created on-the-fly
{% set code = 'Hi {{ name }}' %}
{% set template = template_from_string(code) %}
!
{{ include(template) }}
Templates created on-the-fly
{% set code = 'Hi {{ name }}' %}
{% set template = template_from_string(code) %}
!
{{ include(template) }}
{% extends template %}
{% embed template %} It works here too
Templates created on-the-fly
{{ include(template_from_string(
'Hi {{ name }}'
)) }}
Templates created and modified on-the-fly
{% set code = 'Hi {{ name }}' %}
{% set code = code|replace({ 'Hi': 'Bye' }) %}
!
{% set template = template_from_string(code) %}
!
{{ include(template) }}
Getting the source of any template
{{ source('core/modules/block/templates/
block.html.twig') }}
It gets the source of the
given template without
actually rendering it.
Imagine a site which allows this customization
Section 1
Section 2
Default design
Customizable site
• Users can provide their own Twig snippets to
customize the design and content of some sections.
• Problem: even if customization is restricted to a
group of controlled users (e.g. "editors") you can't
trust those templates.
Twig Sandbox
• It's used to render "untrusted templates".
• It restricts the Twig features that can be used by the
template.
• Useful for letting users create their own templates
and maintain the application safe.
Twig Sandbox in practice
{% sandbox %}
{{ include(section.name ~ '/sidebar.twig') }}
{% endsandbox %}
!
!
{{ include(section.name ~ '/sidebar.twig',
sandboxed = true) }}
Twig Sandbox in practice
$policy = new Twig_Sandbox_SecurityPolicy(
$tags,
$filters,
$methods,
$properties,
$functions
);
!
$sandbox = new Twig_Extension_Sandbox($policy);
$twig->addExtension($sandbox);
Policy is defined as a
white-list of allowed
tags, filters, etc.
Twig Sandbox policy sample
$properties = array(
'label',
'configuration' => array('label', 'module'),
'block' => array('module'),
'attributes',
);
!
$policy = new Twig_Sandbox_SecurityPolicy(
$tags, $filters, $methods, $properties, $functions
);
DATES
WHY IS THIS IMPORTANT??
Because dealing with dates
is not easy and Twig can
perform a lot of operations
on dates.
Timezones support
{{ 'now'|date(timezone='Asia/Tokyo') }}
!
{{ 'now'|date(timezone=user.timezone) }}
Comparing dates
{% if event.startsAt > date('now') %}
Buy tickets
{% endif %}
Comparing dates
{% if event.startsAt > date('now') %}
Buy tickets
{% endif %}
NOTE This is the date( )
function, not the date filter
Modifying dates semantically
Early Bird ends at
{{ event.startsAt|date_modify('-15 days')|date }}
!
Confirm your sign up before
{{ user.createdAt|date_modify('+48 hours')|date }}
COOL
FEATURES
These are some of the
features that put Twig
years ahead of PHP
Useful filters for collections
{{ user.friends|first }}
{{ event.sessions|last }}
Useful tests for strings
{% if url starts with 'https://' %}
{% endif %}
!
{% if file_path ends with '.pdf' %}
{% endif %}
!
{% if phone matches '/^[d.]+$/' %}
{% endif %}
REJECTED BY
PHP
Useful tests for strings
{% if url starts with 'https://' %}
{% endif %}
!
{% if file_path ends with '.pdf' %}
{% endif %}
!
{% if phone matches '/^[d.]+$/' %}
{% endif %}
The "in" operator
{% if password in username %}
BAD PASSWORD
{% endif %}
!
{% if method in ['GET', 'POST'] %}
...
{% endif %}
The "in" operator
{% if password in username %}
BAD PASSWORD
{% endif %}
!
{% if method in ['GET', 'POST'] %}
...
{% endif %}
REJECTED BY
PHP
Named parameters
{{ content|convert_encoding('UTF-8', 'iso-2022-jp') }}
Named parameters
{{ content|convert_encoding('UTF-8', 'iso-2022-jp') }}
which is the original charset and
which one the target charset?
Named parameters
{{ content|convert_encoding('UTF-8', 'iso-2022-jp') }}
which is the original charset and
which one the target charset?
{{ content|convert_encoding(
from = 'UTF-8', to = 'iso-2022-jp'
) }}
Named parameters
{{ content|convert_encoding('UTF-8', 'iso-2022-jp') }}
which is the original charset and
which one the target charset?
{{ content|convert_encoding(
from = 'UTF-8', to = 'iso-2022-jp'
) }}
{{ content|convert_encoding(
to = 'UTF-8', from = 'iso-2022-jp'
) }}
Named parameters
{{ content|convert_encoding('UTF-8', 'iso-2022-jp') }}
PROPOSED FOR
PHP
which is the original charset and
which one the target charset?
{{ content|convert_encoding(
from = 'UTF-8', to = 'iso-2022-jp'
) }}
{{ content|convert_encoding(
to = 'UTF-8', from = 'iso-2022-jp'
) }}
Named parameters
{{ include('template.html', {}, true, true) }}
!
!
!
{{ include('template.html', ignore_missing = true) }}
template variables
with_context
ignore_missing
It's common to do things in batches
1
Image gallery
2 3
4 5 6
The HTML of the image gallery
<div class="row">
<div class="image"> ... </div>
<div class="image"> ... </div>
<div class="image"> ... </div>
</div>
!
<div class="row">
<div class="image"> ... </div>
<div class="image"> ... </div>
<div class="image"> ... </div>
</div>
The template without the "batch" filter
{% for i, image in images %}
{% if i is divisible by(3) %} <div class="row"> {% endif %}
!
<div class="image">
<img src="" alt="" >
<p>...</p>
</div>
!
{% if i is divisible by(3) %} </div> {% endif %}
{% endfor %}
The template without the "batch" filter
{% for i, image in images %}
{% if i is divisible by(3) %} <div class="row"> {% endif %}
!
<div class="image">
<img src="" alt="" >
<p>...</p>
</div>
!
{% if i is divisible by(3) %} </div> {% endif %}
{% endfor %}
UGLY CODE
The template with the "batch" filter
{% for row in images|batch(3) %}
<div class="row">
!
{% for image in row %}
<div class="image">
<img src="" alt="" >
<p>...</p>
</div>
{% endfor %}
!
</div>
{% endfor %}
The template with the "batch" filter
{% for row in images|batch(3) %}
<div class="row">
!
{% for image in row %}
<div class="image">
<img src="" alt="" >
<p>...</p>
</div>
{% endfor %}
!
</div>
{% endfor %}
Short ternary operator
$result = $condition ? 'is true';
Short ternary operator
$result = $condition ? 'is true';
ERROR Parse error: syntax error, unexpected ';' on line 1
Short ternary operator
$result = $condition ? 'is true';
ERROR Parse error: syntax error, unexpected ';' on line 1
{{ condition ? 'is true' }}
Short ternary operator
$result = $condition ? 'is true';
ERROR Parse error: syntax error, unexpected ';' on line 1
OK This works perfectly on Twig
{{ condition ? 'is true' }}
Short ternary operator
<li class="{{ condition ? 'selected' }}">
...
</li>
!
<li class="{{ condition ? 'selected' : '' }}">
...
</li>
Short ternary operator
<li class="{{ condition ? 'selected' }}">
...
</li>
!
<li class="{{ condition ? 'selected' : '' }}">
...
</li>
always use this
Short slice syntax
!
!
!
{{ user.friends[0:3] }}
{{ user.friends[:-3] }}
It combines array_slice, mb_substr
and substr PHP functions.
get first three friends
get last three friends
Short slice syntax
{{ '0123456789'[0:] }} {# 0123456789 #}
{{ '0123456789'[1:] }} {# 123456789 #}
{{ '0123456789'[20:] }} {# (empty) #}
{{ '0123456789'[-5:] }} {# 56789 #}
{{ '0123456789'[-1:] }} {# 9 #}
{{ '0123456789'[1:5] }} {# 12345 #}
{{ '0123456789'[1:-5] }} {# 1234 #}
OUTPUT
The "loop" magic variable
Everyone needs an $i variable inside
the for loop. So Twig provides you
this and other useful variables.
The "loop" variable exists only inside the "for"
{% for ... in collection %}
{{ loop.index }}
{{ loop.index0 }}
{{ loop.first }}
{{ loop.last }}
{{ loop.length }}
{% endfor %}
The "loop" variable exists only inside the "for"
{% for ... in collection %}
{{ loop.index }}
{{ loop.index0 }}
{{ loop.first }}
{{ loop.last }}
{{ loop.length }}
{% endfor %}
1, 2, 3, 4, 5, ...
0, 1, 2, 3, 4, ...
true, false, false, ...
..., false, false, true
5
{{ product.photo|image(400, 150, 0.9) }}
The problem with filter arguments
{{ product.photo|image(400, 150, 0.9) }}
The problem with filter arguments
What if I need to define more arguments?
{{ product.photo|image(400, 150, 0.9) }}
{{ product.photo|image(
width = 400, height = 150, opacity = 0.9
) }}
The problem with filter arguments
What if I need to define more arguments?
{{ product.photo|image(400, 150, 0.9) }}
{{ product.photo|image(
width = 400, height = 150, opacity = 0.9
) }}
The problem with filter arguments
What if I need to define more arguments?
this is a valid solution for Twig, but the
underlying PHP code is still very complex
Defining a filter with lots or arguments
$filter = new Twig_SimpleFilter('image', function (
$path, $width, $height, $opacity
) {
$path = ...
$width = ...
$height = ...
$opacity = ...
});
$filter = new Twig_SimpleFilter('image', function (
$path, $options = array()
) {
$path = ...
$width = $options['width'];
$height = $options['height'];
$opacity = $options['opacity'];
}, array('is_variadic' => true));
Defining a variadic filter
$filter = new Twig_SimpleFilter('image', function (
$path, $options = array()
) {
$path = ...
$width = $options['width'];
$height = $options['height'];
$opacity = $options['opacity'];
}, array('is_variadic' => true));
Defining a variadic filter
a single variadic parameter holds any
number of passed parameters (unlimited)
$filter = new Twig_SimpleFilter('image', function (
$path, $options = array()
) {
$path = ...
$width = $options['width'];
$height = $options['height'];
$opacity = $options['opacity'];
}, array('is_variadic' => true));
ACCEPTED BY
PHP
Defining a variadic filter
a single variadic parameter holds any
number of passed parameters (unlimited)
TO SUM UP
«Using Twig templates
is the best decision
Drupal ever made»
Drupal 8 templates are
safe, concise, modern
and consistent.
Twig
Drupal 8 templates are
safe, concise, modern
and consistent.
Drupal 8 themes
REFERENCES
References
• Official Twig documentation

twig.sensiolabs.org/documentation

• Twig in Drupal 8

drupal.org/theme-guide/8/twig
CONTACT
Contact info
• javier.eguiluz@sensiolabs.com
• github.com/javiereguiluz
• linkedin.com/in/javiereguiluz
!
!
!
Mastering Twig (DrupalCon Barcelona 2015)

Weitere ähnliche Inhalte

Was ist angesagt?

Introduction to Twig
Introduction to TwigIntroduction to Twig
Introduction to Twig
markstory
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
Ignacio Martín
 
Design Patterns in PHP5
Design Patterns in PHP5 Design Patterns in PHP5
Design Patterns in PHP5
Wildan Maulana
 
Creating and Maintaining WordPress Plugins
Creating and Maintaining WordPress PluginsCreating and Maintaining WordPress Plugins
Creating and Maintaining WordPress Plugins
Mark Jaquith
 

Was ist angesagt? (19)

TWIG: the flexible, fast and secure template language for PHP
TWIG: the flexible, fast and secure template language for PHPTWIG: the flexible, fast and secure template language for PHP
TWIG: the flexible, fast and secure template language for PHP
 
Codeware
CodewareCodeware
Codeware
 
Introduction to Twig
Introduction to TwigIntroduction to Twig
Introduction to Twig
 
Design patterns in PHP
Design patterns in PHPDesign patterns in PHP
Design patterns in PHP
 
Symfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worldsSymfony & Javascript. Combining the best of two worlds
Symfony & Javascript. Combining the best of two worlds
 
Geek Moot '09 -- Smarty 101
Geek Moot '09 -- Smarty 101Geek Moot '09 -- Smarty 101
Geek Moot '09 -- Smarty 101
 
Design Patterns in PHP5
Design Patterns in PHP5 Design Patterns in PHP5
Design Patterns in PHP5
 
Object Oriented PHP5
Object Oriented PHP5Object Oriented PHP5
Object Oriented PHP5
 
Functions in PHP
Functions in PHPFunctions in PHP
Functions in PHP
 
Php Tutorials for Beginners
Php Tutorials for BeginnersPhp Tutorials for Beginners
Php Tutorials for Beginners
 
10 PHP Design Patterns #burningkeyboards
10 PHP Design Patterns #burningkeyboards10 PHP Design Patterns #burningkeyboards
10 PHP Design Patterns #burningkeyboards
 
PHP Enums - PHPCon Japan 2021
PHP Enums - PHPCon Japan 2021PHP Enums - PHPCon Japan 2021
PHP Enums - PHPCon Japan 2021
 
Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3Dependency Injection with PHP 5.3
Dependency Injection with PHP 5.3
 
Data Validation models
Data Validation modelsData Validation models
Data Validation models
 
Creating and Maintaining WordPress Plugins
Creating and Maintaining WordPress PluginsCreating and Maintaining WordPress Plugins
Creating and Maintaining WordPress Plugins
 
Maintainable JavaScript 2012
Maintainable JavaScript 2012Maintainable JavaScript 2012
Maintainable JavaScript 2012
 
Class 2 - Introduction to PHP
Class 2 - Introduction to PHPClass 2 - Introduction to PHP
Class 2 - Introduction to PHP
 
PHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better CodePHP: 4 Design Patterns to Make Better Code
PHP: 4 Design Patterns to Make Better Code
 
PHP 8.1 - What's new and changed
PHP 8.1 - What's new and changedPHP 8.1 - What's new and changed
PHP 8.1 - What's new and changed
 

Andere mochten auch

Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8
Javier Eguiluz
 
Twig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasTwig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadas
Javier Eguiluz
 
sfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin BundlesfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin Bundle
th0masr
 

Andere mochten auch (20)

New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)New Symfony Tips & Tricks (SymfonyCon Paris 2015)
New Symfony Tips & Tricks (SymfonyCon Paris 2015)
 
Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8Twig, el nuevo motor de plantillas de Drupal 8
Twig, el nuevo motor de plantillas de Drupal 8
 
Symfony tips and tricks
Symfony tips and tricksSymfony tips and tricks
Symfony tips and tricks
 
Symfony Best Practices
Symfony Best PracticesSymfony Best Practices
Symfony Best Practices
 
30 Symfony Best Practices
30 Symfony Best Practices30 Symfony Best Practices
30 Symfony Best Practices
 
Twig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadasTwig, los mejores trucos y técnicas avanzadas
Twig, los mejores trucos y técnicas avanzadas
 
Arquitectura para artesanos
Arquitectura para artesanosArquitectura para artesanos
Arquitectura para artesanos
 
Php 101: PDO
Php 101: PDOPhp 101: PDO
Php 101: PDO
 
Software craftsmanship coaching
Software craftsmanship coachingSoftware craftsmanship coaching
Software craftsmanship coaching
 
#PhpirstAid - Replanteamiento de diseño de software
#PhpirstAid - Replanteamiento de diseño de software#PhpirstAid - Replanteamiento de diseño de software
#PhpirstAid - Replanteamiento de diseño de software
 
JavaScript
JavaScriptJavaScript
JavaScript
 
Introduzione a JavaScript
Introduzione a JavaScriptIntroduzione a JavaScript
Introduzione a JavaScript
 
Have you played this Symfony? Why Symfony is great choice for Web development
Have you played this Symfony? Why Symfony is great choice for Web developmentHave you played this Symfony? Why Symfony is great choice for Web development
Have you played this Symfony? Why Symfony is great choice for Web development
 
Symfony2: 30 astuces et bonnes pratiques
Symfony2: 30 astuces et bonnes pratiquesSymfony2: 30 astuces et bonnes pratiques
Symfony2: 30 astuces et bonnes pratiques
 
Design patterns avec Symfony
Design patterns avec SymfonyDesign patterns avec Symfony
Design patterns avec Symfony
 
Angular js o React? Spunti e idee per la scelta di un framework
Angular js o React? Spunti e idee per la scelta di un frameworkAngular js o React? Spunti e idee per la scelta di un framework
Angular js o React? Spunti e idee per la scelta di un framework
 
Symfony Components
Symfony ComponentsSymfony Components
Symfony Components
 
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years laterSymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
SymfonyCon Berlin 2016 - Symfony Plugin for PhpStorm - 3 years later
 
Hexagonal architecture - message-oriented software design
Hexagonal architecture  - message-oriented software designHexagonal architecture  - message-oriented software design
Hexagonal architecture - message-oriented software design
 
sfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin BundlesfDay Cologne - Sonata Admin Bundle
sfDay Cologne - Sonata Admin Bundle
 

Ähnlich wie Mastering Twig (DrupalCon Barcelona 2015)

Presentation drupalaton august 2013
Presentation drupalaton august 2013Presentation drupalaton august 2013
Presentation drupalaton august 2013
Bram Goffings
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
Fabien Potencier
 
Powerful and flexible templates with Twig
Powerful and flexible templates with Twig Powerful and flexible templates with Twig
Powerful and flexible templates with Twig
Michael Peacock
 

Ähnlich wie Mastering Twig (DrupalCon Barcelona 2015) (20)

Twig: Friendly Curly Braces Invade Your Templates!
Twig: Friendly Curly Braces Invade Your Templates!Twig: Friendly Curly Braces Invade Your Templates!
Twig: Friendly Curly Braces Invade Your Templates!
 
Drupalcamp Leuven 2013 - Display Suite, the future of your display
Drupalcamp Leuven 2013 - Display Suite, the future of your displayDrupalcamp Leuven 2013 - Display Suite, the future of your display
Drupalcamp Leuven 2013 - Display Suite, the future of your display
 
Being Dangerous with Twig
Being Dangerous with TwigBeing Dangerous with Twig
Being Dangerous with Twig
 
Presentation drupalaton august 2013
Presentation drupalaton august 2013Presentation drupalaton august 2013
Presentation drupalaton august 2013
 
Presentation drupalaton, August 2013
Presentation drupalaton, August 2013Presentation drupalaton, August 2013
Presentation drupalaton, August 2013
 
Being Dangerous with Twig (Symfony Live Paris)
Being Dangerous with Twig (Symfony Live Paris)Being Dangerous with Twig (Symfony Live Paris)
Being Dangerous with Twig (Symfony Live Paris)
 
The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010The state of Symfony2 - SymfonyDay 2010
The state of Symfony2 - SymfonyDay 2010
 
Grails Worst Practices
Grails Worst PracticesGrails Worst Practices
Grails Worst Practices
 
Into The Box 2018 - CBT
Into The Box 2018 - CBTInto The Box 2018 - CBT
Into The Box 2018 - CBT
 
Powerful and flexible templates with Twig
Powerful and flexible templates with Twig Powerful and flexible templates with Twig
Powerful and flexible templates with Twig
 
Pruexx User's guide for beta testing
Pruexx User's guide for beta testingPruexx User's guide for beta testing
Pruexx User's guide for beta testing
 
Web-First Design Patterns
Web-First Design PatternsWeb-First Design Patterns
Web-First Design Patterns
 
Twig for Drupal 8 and PHP | Presented at OC Drupal
Twig for Drupal 8 and PHP | Presented at OC DrupalTwig for Drupal 8 and PHP | Presented at OC Drupal
Twig for Drupal 8 and PHP | Presented at OC Drupal
 
Enhance your WordPress development with Twig through Clarkson - WordCamp Barc...
Enhance your WordPress development with Twig through Clarkson - WordCamp Barc...Enhance your WordPress development with Twig through Clarkson - WordCamp Barc...
Enhance your WordPress development with Twig through Clarkson - WordCamp Barc...
 
Micropatterns
MicropatternsMicropatterns
Micropatterns
 
Twig
TwigTwig
Twig
 
Symfony 4 Workshop - Limenius
Symfony 4 Workshop - LimeniusSymfony 4 Workshop - Limenius
Symfony 4 Workshop - Limenius
 
Twig for Drupal @ Frontendunited Amsterdam 2012
Twig for Drupal @ Frontendunited Amsterdam 2012Twig for Drupal @ Frontendunited Amsterdam 2012
Twig for Drupal @ Frontendunited Amsterdam 2012
 
Mastering Drupal 8’s Twig
Mastering Drupal 8’s TwigMastering Drupal 8’s Twig
Mastering Drupal 8’s Twig
 
Metaprogramming
MetaprogrammingMetaprogramming
Metaprogramming
 

Mehr von Javier Eguiluz

Mehr von Javier Eguiluz (18)

deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de SymfonydeSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
deSymfony 2017: Symfony 4, Symfony Flex y el futuro de Symfony
 
Silex al límite
Silex al límiteSilex al límite
Silex al límite
 
Silex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHPSilex, desarrollo web ágil y profesional con PHP
Silex, desarrollo web ágil y profesional con PHP
 
Wallpaper Notifier
Wallpaper NotifierWallpaper Notifier
Wallpaper Notifier
 
Backend (sf2Vigo)
Backend (sf2Vigo)Backend (sf2Vigo)
Backend (sf2Vigo)
 
Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)Twig avanzado (sf2Vigo)
Twig avanzado (sf2Vigo)
 
Desymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseñoDesymfony 2012 - Concurso de diseño
Desymfony 2012 - Concurso de diseño
 
Desymfony 2011 - Twig
Desymfony 2011 - TwigDesymfony 2011 - Twig
Desymfony 2011 - Twig
 
Desymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: BackendDesymfony 2011 - Tutorial #5: Backend
Desymfony 2011 - Tutorial #5: Backend
 
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasosDesymfony 2011 - Tutorial #1: Instalacion y primeros pasos
Desymfony 2011 - Tutorial #1: Instalacion y primeros pasos
 
Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2Desymfony 2011 - Introducción a Symfony2
Desymfony 2011 - Introducción a Symfony2
 
Symfony2, Jornadas Symfony
Symfony2, Jornadas SymfonySymfony2, Jornadas Symfony
Symfony2, Jornadas Symfony
 
Curso Symfony - Anexos
Curso Symfony - AnexosCurso Symfony - Anexos
Curso Symfony - Anexos
 
Curso Symfony - Clase 5
Curso Symfony - Clase 5Curso Symfony - Clase 5
Curso Symfony - Clase 5
 
Curso Symfony - Clase 4
Curso Symfony - Clase 4Curso Symfony - Clase 4
Curso Symfony - Clase 4
 
Curso Symfony - Clase 3
Curso Symfony - Clase 3Curso Symfony - Clase 3
Curso Symfony - Clase 3
 
Curso Symfony - Clase 2
Curso Symfony - Clase 2Curso Symfony - Clase 2
Curso Symfony - Clase 2
 
Curso Symfony - Clase 1
Curso Symfony - Clase 1Curso Symfony - Clase 1
Curso Symfony - Clase 1
 

Kürzlich hochgeladen

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 

Kürzlich hochgeladen (20)

AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdfUnderstanding Discord NSFW Servers A Guide for Responsible Users.pdf
Understanding Discord NSFW Servers A Guide for Responsible Users.pdf
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law DevelopmentsTrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
TrustArc Webinar - Stay Ahead of US State Data Privacy Law Developments
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
Tech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdfTech Trends Report 2024 Future Today Institute.pdf
Tech Trends Report 2024 Future Today Institute.pdf
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 

Mastering Twig (DrupalCon Barcelona 2015)

  • 1. Javier EguiluzSeptember 22, 2015 Twig TRACK ROOM DATE SPEAKER Symfony 115 Mastering
  • 2. License of this presentation creativecommons.org/licenses/by-nc-sa/3.0
  • 6. We won't talk about Twig basics. We won't provide all the low-level details. How can I create a theme? Which is the syntax of Twig? Read the excellent Twig documentation to get those details.
  • 8. Fast Easy to learn Documented Concise Full featured Extensible Tested Useful errors Secure Main Twig features
  • 10. My favorite feature Twig defines a very small set of features… … which are enough to create any template Consistent same syntax and behavior since day one! easy to learn!
  • 11. «Using Twig templates is the best decision Drupal ever made»
  • 12. DRUPAL 8 TWIG Built-in Drupal templates use ~30% of the available Twig features.
  • 16. WHY IS THIS IMPORTANT?? Because you can easily improve the performance of your site/app.
  • 17. Accessing simple variables ! <p class="comment__author">{{ author }}</p> <p class="comment__time">{{ created }}</p> <p class="comment__permalink">{{ permalink }}</p> core/themes/bartik/templates/comment.html.twig
  • 18. Accessing complex variables ! <nav>{{ content.links }}</nav> core/themes/bartik/templates/comment.html.twig
  • 19. Accessing complex variables ! <nav>{{ content.links }}</nav> core/themes/bartik/templates/comment.html.twig
  • 20. This is how Twig resolves complex variables <nav>{{ content.links }}</nav> ! $content['links'] $content->links $content->links() $content->getLinks() $content->isLinks() null Twig tries all these alternatives and uses the first one that exists.
  • 22. Resolving variables is quite expensive <nav>{{ content.links }}</nav> ! $content['links'] $content->links $content->links() $content->getLinks() $content->isLinks() null Resolving a variable is the most expensive Twig task, specially for very complex templates.
  • 23. Improving Twig performance • Twig provides a PHP extension. • This extension only implements the variable resolving logic. • See twig.sensiolabs.org/doc/ installation.html#installing-the- c-extension EXPECTED
 PERFORMANCE INCREASE 15%
  • 24. Some Drupal variables names are special ! $variables['site_slogan']['#markup'] = ... ! {{ site_slogan.#markup }} core/themes/bartik/bartik.theme This doesn't work because of the # character
  • 25. Some Drupal variables names are special ! $variables['site_slogan']['#markup'] = ... ! {{ site_slogan.#markup }} core/themes/bartik/bartik.theme This doesn't work because of the # character {{ site_slogan['#markup'] }} {{ attribute(site_slogan, '#markup') }}
  • 27. WHY IS THIS IMPORTANT?? Because sooner or later errors will happen. What matters is how you deal with them.
  • 28. Dealing with undefined/empty variables is empty defaultis defined is null {% if %}
  • 29. The two recommended safeguards {% if variable %} ... {% endif %} ! ! Hi {{ variable|default('user') }}
  • 30. The two recommended safeguards {% if variable %} ... {% endif %} ! ! Hi {{ variable|default('user') }} It checks that variable is not null or empty or zero ! ONLY works if variable is defined
  • 31. The two recommended safeguards {% if variable %} ... {% endif %} ! ! Hi {{ variable|default('user') }} It checks that variable is not null or empty or zero ! ONLY works if variable is defined It checks that variable is not null, empty or undefined ! It ALWAYS works as expected
  • 32. Combining both safeguards {% if variable|default('user') %} ... {% endif %} It doesn't matter if the variable is not defined, because the expression will always have a default value.
  • 33. Checking that the variable is defined {% if variable is defined %} ... {% endif %} A good practice when the rendered template cannot be sure about the variables passed from the code. ! In Drupal 8 this problem should not happen (the variable list is strict).
  • 34. Other safeguards available {% if variable is null %} ... {% endif %} ! ! {% if variable is empty %} ... {% endif %} {% if variable is not empty %} ... {% endif %}
  • 35. Be ready when iterating empty collections {% for item in collection %} ... {% else %} There are no items. {% endfor %}
  • 36. Filter values before using them in the loop {% for item in collection if item.published %} ... {% else %} There are no items. {% endfor %}
  • 37. Avoid missing templates {{ include('menu.twig') }} This will always work because our theme will provide this template.
  • 38. Avoid missing templates {{ include('menu.twig') }} This will always work because our theme will provide this template. Templates with dynamic paths are very prone to error {{ include('users/' ~ user.name ~ '/bio.twig') }}
  • 39. Define fallback templates {{ include([ 'users/' ~ user.name ~ '/bio.twig', 'users/' ~ user.name ~ '/default.twig', 'common/user_bio.twig' ]) }} Twig includes the first template that exists
  • 40. Avoid missing templates • Sometimes it's not possible to provide fallback templates. • Moreover, in some cases, it's better to ignore the missing template instead of displaying an error to the user.
  • 41. Ignore missing templates {{ include('template.twig', ignore_missing = true) }} ! {{ source('template.twig', ignore_missing = true) }} ! {% embed 'template.twig' ignore missing %} ... {% endembed %}
  • 42. Ignore missing templates {{ include('template.twig', ignore_missing = true) }} ! {{ source('template.twig', ignore_missing = true) }} ! {% embed 'template.twig' ignore missing %} ... {% endembed %} NOTE no underscore here
  • 43. Twig filters defined by Drupal 8 {{ value|t }} {{ value|trans }} {{ value|passthrough }} {{ value|placeholder }} {{ value|drupal_escape }} {{ value|safe_join }} {{ value|without }} {{ value|clean_class }} {{ value|clean_id }} {{ value|render }} It's common for a long- standing and complex project to add and remove filters. ! If Drupal removes a filter used by your templates, your site/app will break.
  • 44. Declare filters as deprecated new Twig_SimpleFilter('old_filter', ..., array( 'deprecated' => true, 'alternative' => 'new_filter' ));
  • 45. Declare filters as deprecated new Twig_SimpleFilter('old_filter', ..., array( 'deprecated' => true, 'alternative' => 'new_filter' )); These deprecations notices are not displayed or logged anywhere on Drupal yet. NOTE
  • 46. Avoid missing blocks {% if 'title' is block %} <title>{{ block('title') }}<title> {% endif %}
  • 47. This feature is not available yet. It will be included in the upcoming 1.23 version of Twig. NOTE Avoid missing blocks {% if 'title' is block %} <title>{{ block('title') }}<title> {% endif %}
  • 49. WHY IS THIS IMPORTANT?? Because it will make your templates more readable and it will save you time.
  • 51. <ul> {% for i in 1..3 %} <li>{{ i }}</li> {% endfor %} </ul> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> The "problem" of white spaces Twig template HTML page
  • 52. <ul> {% for i in 1..3 %} <li>{{ i }}</li> {% endfor %} </ul> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> The "problem" of white spaces Twig template HTML page
  • 53. <ul> {% for i in 1..3 %} <li>{{ i }}</li> {% endfor %} </ul> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> The "problem" of white spaces Twig template HTML page
  • 54. <ul> {% for i in 1..3 %} <li>{{ i }}</li> {% endfor %} </ul> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> The "problem" of white spaces Twig template HTML page
  • 55. <ul> {% for i in 1..3 %} <li>{{ i }}</li> {% endfor %} </ul> <ul> <li>1</li> <li>2</li> <li>3</li> </ul> The "problem" of white spaces Twig template HTML page
  • 56. Removing white spaces <ul> {%- for i in 1..3 -%} <li>{{ i }}</li> {%- endfor -%} </ul> <ul> {% spaceless %} {% for i in 1..3 %} <li>{{ i }}</li> {% endfor %} {% endspaceless %} </ul>
  • 57. Please, don't waste your time dealing with white spaces.
  • 58. ! Twig templates should be readable HTML pages should not !
  • 59. ! Twig templates should be readable HTML pages should not this is where you work everyday ! browsers get a minimized and compressed HTML mess
  • 60. Sometimes you should add white spaces…
  • 61. White spaces around HTML attributes <h2{{ title_attributes }}>{{ label }}</h2> core/modules/block/templates/block.html.twig
  • 62. White spaces around HTML attributes <h2{{ title_attributes }}>{{ label }}</h2> core/modules/block/templates/block.html.twig
  • 63. White spaces around HTML attributes <h2{{ title_attributes }}>{{ label }}</h2> core/modules/block/templates/block.html.twig no white space when the attributes are empty <h2 class="..."> ... </h2> <h2> ... </h2>
  • 64. Add white spaces to separate Twig & HTML <h2 {{ title_attributes }}>{{ label }}</h2> ! ! <h2 class="..."> ... </h2> <h2 > ... </h2>
  • 65. Add white spaces to separate Twig & HTML <h2 {{ title_attributes }}>{{ label }}</h2> ! ! <h2 class="..."> ... </h2> <h2 > ... </h2> white space when the attributes are empty
  • 66. Add white spaces to separate Twig & HTML <h2 {{ title_attributes }}>{{ label }}</h2> ! ! <h2 class="..."> ... </h2> <h2 > ... </h2> white space when the attributes are empty IT DOES NOT MATTER Twig template is more readable HTML code with white spaces is still valid
  • 67. Hiding HTML code inside Twig strings <div id="site-name"{{ hide_name ? ' class="hidden"' }}> ! ! ! ! ! <div id="site-name"> <div id="site-name" class="hidden"> core/themes/bartik/templates/maintenance-page.html.twig
  • 68. Hiding HTML code inside Twig strings <div id="site-name"{{ hide_name ? ' class="hidden"' }}> ! ! ! ! ! <div id="site-name"> <div id="site-name" class="hidden"> core/themes/bartik/templates/maintenance-page.html.twig
  • 69. Hiding HTML code inside Twig strings <div id="site-name"{{ hide_name ? ' class="hidden"' }}> ! ! ! ! ! <div id="site-name"> <div id="site-name" class="hidden"> WARNING HTML attributes defined in Twig strings are easy to overlook core/themes/bartik/templates/maintenance-page.html.twig
  • 70. Hiding HTML code inside Twig strings <div id="site-name"{{ hide_name ? ' class="hidden"' }}> ! ! ! ! ! <div id="site-name"> <div id="site-name" class="hidden"> WARNING HTML attributes defined in Twig strings are easy to overlook core/themes/bartik/templates/maintenance-page.html.twig DANGER If you miss this single white space, the page design breaks
  • 71. Don't hide HTML code inside Twig strings <div id="site-name" class="{{ hide_name ? 'hidden' }}"> ! ! ! ! ! <div id="site-name" class=""> <div id="site-name" class="hidden"> HTML & Twig are decoupled A single white space won't break the page
  • 72. Don't hide HTML code inside Twig strings <div id="site-name" class="{{ hide_name ? 'hidden' }}"> ! ! ! ! ! <div id="site-name" class=""> <div id="site-name" class="hidden"> Valid HTML code (tested with the W3C validator) HTML & Twig are decoupled A single white space won't break the page
  • 73. DEBUG
  • 74. WHY IS THIS IMPORTANT?? Because it will save you a lot of time while developing your templates.
  • 75. Configure Twig behavior # sites/default/services.yml parameters: twig.config: debug: true auto_reload: null cache: true
  • 76. Configure Twig behavior # sites/default/services.yml parameters: twig.config: debug: true auto_reload: null cache: true Include debug information in the rendered HTML contents In production server, always set it to false
  • 77. HTML content of a rendered Drupal template <div id="block-bartik-login" class="contextual-region block block-user block-user-login-block" role="form"> <h2>User login</h2> <div data-contextual-id="block:block=bartik_login:langcode=en"></div> <div class="content"> ! <form class="user-login-form" data-drupal-selector="user-login-form" action="/node?destination=/ node" method="post" id="user-login-form" accept-charset="UTF-8"> ! <!-- ... -->
  • 78. HTML content when Twig debug is enabled <!-- THEME DEBUG --> <!-- THEME HOOK: 'block' --> <!-- FILE NAME SUGGESTIONS: * block--bartik-login.html.twig * block--user-login-block.html.twig * block--user.html.twig x block.html.twig --> <!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' --> <div id="block-bartik-login" class="contextual-region block block-user block-user-login-block" role="form"> <h2>User login</h2> <div data-contextual-id="block:block=bartik_login:langcode=en"></div> <div class="content"> ! <!-- THEME DEBUG --> <!-- THEME HOOK: 'form' --> <!-- BEGIN OUTPUT from 'core/themes/classy/templates/form/form.html.twig' --> <form class="user-login-form" data-drupal-selector="user-login-form" action="/node?destination=/
  • 79. How to override the current template <!-- THEME DEBUG --> <!-- THEME HOOK: 'block' --> <!-- FILE NAME SUGGESTIONS: * block--bartik-login.html.twig * block--user-login-block.html.twig * block--user.html.twig x block.html.twig --> <!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' -->
  • 80. How to override the current template <!-- THEME DEBUG --> <!-- THEME HOOK: 'block' --> <!-- FILE NAME SUGGESTIONS: * block--bartik-login.html.twig * block--user-login-block.html.twig * block--user.html.twig x block.html.twig --> <!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' --> Drupal tried to use all these templates…
  • 81. How to override the current template <!-- THEME DEBUG --> <!-- THEME HOOK: 'block' --> <!-- FILE NAME SUGGESTIONS: * block--bartik-login.html.twig * block--user-login-block.html.twig * block--user.html.twig x block.html.twig --> <!-- BEGIN OUTPUT from 'core/themes/bartik/templates/block.html.twig' --> …before deciding to use this template. Drupal tried to use all these templates…
  • 82. Which variables are passed to the template? Built-in templates include comments with the full list of variables passed to the Twig template.
  • 83. Easier way to introspect all variables <pre> {{ dump() }} </pre>
  • 84. Easier way to introspect all variables <pre> {{ dump() }} </pre> It dumps the contents of all the variables defined in the template.
  • 85. It's better to dump just the variables you need <pre> {{ dump(label, title_attributes) }} </pre>
  • 86. It's better to dump just the variables you need <pre> {{ dump(label, title_attributes) }} </pre> It dumps only the given variables
  • 87. CAUTION! Don't forget to rebuild your cache after changing the config files and templates.
  • 91. WHY IS THIS IMPORTANT?? Because it can prevent you a lot of security-related problems.
  • 92. CAUTION! Drupal has replaced the default Twig escaping filter by their own.
  • 93. By default, contents are escaped for HTML Hi {{ content }}! $content = '<strong>John</strong>';
  • 94. By default, contents are escaped for HTML Hi {{ content }}! $content = '<strong>John</strong>'; What you expect… Hi John! What you get… Hi <strong>John </strong>!
  • 95. The "raw" filter prevents the escaping Hi {{ content|raw }}! $content = '<strong>John</strong>';
  • 96. The "raw" filter prevents the escaping Hi {{ content|raw }}! $content = '<strong>John</strong>'; What you expect… Hi John! What you get… Hi John!
  • 97. What if contents are used in URLs or JS? <a href="...?param={{ value }}"></a> ! ! ! <script> var variable = "{{ content }}"; </script>
  • 98. What if contents are used in URLs or JS? <a href="...?param={{ value }}"></a> ! ! ! <script> var variable = "{{ content }}"; </script> WRONG HTML ESCAPING
  • 99. Applying different escaping strategies <a href="...?param={{ value|e('url') }}"></a> ! ! ! <script> var variable = "{{ content|e('js') }}"; </script>
  • 100. Escaping strategies available in Twig {{ content|e('html') }} {{ content|e('js') }} {{ content|e('css') }} {{ content|e('url') }} {{ content|e('html_attr') }}
  • 102. WHY IS THIS IMPORTANT?? Because it allows you to avoid repeating code and it makes your themes easier to maintain.
  • 103. Lots of different ways to reuse templates {% embed %} {% extends %}include() {% set %} {% use %}macro()
  • 104. How often are these alternatives used {% embed %} {% extends %} include( ) {% set %} {% use %} macro( ) Always Sometimes Rarely
  • 105. Lots of different ways to reuse templates {% embed %} {% extends %}include() {% set %} {% use %}macro()
  • 106. Use {% extends %} to share layouts
  • 107. Use {% extends %} to share layouts 1 layout with the common design elements
  • 108. Use {% extends %} to share layouts 1 layout with the common design elements + 4 simple pages which only define their contents
  • 109. layout.twig <!DOCTYPE html> <html> <head> <title> {% block title %}ACME website{% endblock %} </title> </head> <body> <div class="container"> {% block content %}{% endblock %} </div> </body> </html>
  • 110. layout.twig <!DOCTYPE html> <html> <head> <title> {% block title %}ACME website{% endblock %} </title> </head> <body> <div class="container"> {% block content %}{% endblock %} </div> </body> </html>
  • 111. Other templates can reuse this layout {% extends 'layout.twig' %} ! {% block title %}Community{% endblock %} {% block content %} <div> ... </div> {% endblock %}
  • 112. When should you use {% extends %} • To create the layout of your theme. • If your site/app is very complex, create two inheritance levels (base layout and section layouts). layout.twig schedule.twig training.twig extends layout.twig extends schedule.twig
  • 113. Lots of different ways to reuse templates {% embed %} {% extends %}include() {% set %} {% use %}macro()
  • 114. Reusing templates with include( ) ! {{ include('listing.twig') }} ! ! <div> {% for item in items %} <h2>{{ item.title }}</h2> <p>{{ item.content }}</p> {% endfor %} </div> blog/index.twig blog/listing.twig
  • 115. When should you use include( ) • To reuse large fragments of code, such as sidebars, navigation menus, etc.
  • 116. Lots of different ways to reuse templates {% embed %} {% extends %}include() {% set %} {% use %}macro()
  • 117. Repetitive HTML fragments <div class="form-group"> <label for="{{ id }}">{{ label }}</label> <input type="{{ type }}" class="form-control" id="{{ id }}"> </div> Repeating the same HTML code for all the form fields is cumbersome
  • 118. Reusing fragments with macro( ) {% macro form_field(id, label, type="text") %} <div class="form-group"> <label for="{{ id }}">{{ label }}</label> <input type="{{ type }}" class="form-control" id="{{ id }}"> </div> {% endmacro %}
  • 119. Using "macros" inside templates {% import _self as macro %} ! <form> {{ macro.form_field('first_name', 'First Name') }} {{ macro.form_field('last_name', 'Last Name') }} {{ macro.form_field('email', 'Email', 'email') }} ... </form>
  • 120. Using "macros" inside templates {% import _self as macro %} ! <form> {{ macro.form_field('first_name', 'First Name') }} {{ macro.form_field('last_name', 'Last Name') }} {{ macro.form_field('email', 'Email', 'email') }} ... </form> Before using a macro, you must "import" them (they can be defined in a different template)
  • 121. When should you use macro( ) • To reuse short fragments of code, usually in the same template (e.g. listings, grids, forms, etc.) • If your site/app is very complex, store all the macros in a single file and reuse it from any other template.
  • 122. Lots of different ways to reuse templates {% embed %} {% extends %}include() {% set %} {% use %}macro()
  • 123. Grid-based design embed allows to reuse inner page structures (e.g. the 3-column grid)
  • 124. Grid-based design embed allows to reuse inner page structures (e.g. the 3-column grid)
  • 125. You can't use "include" to solve this problem {{ include('common/grid_3.twig') }}
  • 126. You can't use "include" to solve this problem {{ include('common/grid_3.twig') }} you can't change the included contents (you include both the structure and the content)
  • 127. You can't use "extends" to solve this problem {% extends 'common/grid_3.twig' %} ! {% block column1 %} ... {% endblock %} {% block column2 %} ... {% endblock %} {% block column3 %} ... {% endblock %}
  • 128. You can't use "extends" to solve this problem {% extends 'common/grid_3.twig' %} ! {% block column1 %} ... {% endblock %} {% block column2 %} ... {% endblock %} {% block column3 %} ... {% endblock %} you can't make the whole structure of the page (grid 2, grid 3, etc.) because you can't extend from multiple templates at the same time
  • 129. Define a three-column grid template <div class="row"> <div class="col-md-4"> {% block column1 %}{% endblock %} </div> ! <div class="col-md-4"> {% block column2 %}{% endblock %} </div> ! <div class="col-md-4"> {% block column3 %}{% endblock %} </div> </div>
  • 130. Reuse the three-column grid template {% embed 'common/grid_3.twig' %} {% block column1 %} ... contents ... {% endblock %} ! {% block column2 %} ... contents ... {% endblock %} ! {% block column3 %} ... contents ... {% endblock %} {% endembed %}
  • 131. When should you use {% embed %} • To reuse page structures across different templates (e.g. grids)
  • 132. Lots of different ways to reuse templates {% embed %} {% extends %}include() {% set %} {% use %}macro()
  • 133. Reusing fragments with {% set %} {% set navigation %} <a href="...">Previous</a> ... ... <a href="...">Next</a> {% endset %}
  • 134. Reusing fragments with {% set %} {% set navigation %} <a href="...">Previous</a> ... ... <a href="...">Next</a> {% endset %} {{ navigation }} {{ navigation }}
  • 135. When should you use {% set %} • To reuse short fragments of code inside a template (if those fragments are configurable, use a macro). • It's like an internal include() made from inside the template itself.
  • 136. Lots of different ways to reuse templates {% embed %} {% extends %}include() {% set %} {% use %}macro()
  • 137. When should you use {% use %} • This is too advanced and for very specific use cases. • You should probably never use it when creating themes.
  • 139. WHY IS THIS IMPORTANT?? Because Drupal allows to create sites with very advanced needs.
  • 140. Templates created on-the-fly {% set code = 'Hi {{ name }}' %} {% set template = template_from_string(code) %} ! {{ include(template) }}
  • 141. Templates created on-the-fly {% set code = 'Hi {{ name }}' %} {% set template = template_from_string(code) %} ! {{ include(template) }} {% extends template %} {% embed template %} It works here too
  • 142. Templates created on-the-fly {{ include(template_from_string( 'Hi {{ name }}' )) }}
  • 143. Templates created and modified on-the-fly {% set code = 'Hi {{ name }}' %} {% set code = code|replace({ 'Hi': 'Bye' }) %} ! {% set template = template_from_string(code) %} ! {{ include(template) }}
  • 144. Getting the source of any template {{ source('core/modules/block/templates/ block.html.twig') }} It gets the source of the given template without actually rendering it.
  • 145. Imagine a site which allows this customization Section 1 Section 2 Default design
  • 146. Customizable site • Users can provide their own Twig snippets to customize the design and content of some sections. • Problem: even if customization is restricted to a group of controlled users (e.g. "editors") you can't trust those templates.
  • 147. Twig Sandbox • It's used to render "untrusted templates". • It restricts the Twig features that can be used by the template. • Useful for letting users create their own templates and maintain the application safe.
  • 148. Twig Sandbox in practice {% sandbox %} {{ include(section.name ~ '/sidebar.twig') }} {% endsandbox %} ! ! {{ include(section.name ~ '/sidebar.twig', sandboxed = true) }}
  • 149. Twig Sandbox in practice $policy = new Twig_Sandbox_SecurityPolicy( $tags, $filters, $methods, $properties, $functions ); ! $sandbox = new Twig_Extension_Sandbox($policy); $twig->addExtension($sandbox); Policy is defined as a white-list of allowed tags, filters, etc.
  • 150. Twig Sandbox policy sample $properties = array( 'label', 'configuration' => array('label', 'module'), 'block' => array('module'), 'attributes', ); ! $policy = new Twig_Sandbox_SecurityPolicy( $tags, $filters, $methods, $properties, $functions );
  • 151. DATES
  • 152. WHY IS THIS IMPORTANT?? Because dealing with dates is not easy and Twig can perform a lot of operations on dates.
  • 153. Timezones support {{ 'now'|date(timezone='Asia/Tokyo') }} ! {{ 'now'|date(timezone=user.timezone) }}
  • 154. Comparing dates {% if event.startsAt > date('now') %} Buy tickets {% endif %}
  • 155. Comparing dates {% if event.startsAt > date('now') %} Buy tickets {% endif %} NOTE This is the date( ) function, not the date filter
  • 156. Modifying dates semantically Early Bird ends at {{ event.startsAt|date_modify('-15 days')|date }} ! Confirm your sign up before {{ user.createdAt|date_modify('+48 hours')|date }}
  • 158. These are some of the features that put Twig years ahead of PHP
  • 159. Useful filters for collections {{ user.friends|first }} {{ event.sessions|last }}
  • 160. Useful tests for strings {% if url starts with 'https://' %} {% endif %} ! {% if file_path ends with '.pdf' %} {% endif %} ! {% if phone matches '/^[d.]+$/' %} {% endif %}
  • 161. REJECTED BY PHP Useful tests for strings {% if url starts with 'https://' %} {% endif %} ! {% if file_path ends with '.pdf' %} {% endif %} ! {% if phone matches '/^[d.]+$/' %} {% endif %}
  • 162. The "in" operator {% if password in username %} BAD PASSWORD {% endif %} ! {% if method in ['GET', 'POST'] %} ... {% endif %}
  • 163. The "in" operator {% if password in username %} BAD PASSWORD {% endif %} ! {% if method in ['GET', 'POST'] %} ... {% endif %} REJECTED BY PHP
  • 165. Named parameters {{ content|convert_encoding('UTF-8', 'iso-2022-jp') }} which is the original charset and which one the target charset?
  • 166. Named parameters {{ content|convert_encoding('UTF-8', 'iso-2022-jp') }} which is the original charset and which one the target charset? {{ content|convert_encoding( from = 'UTF-8', to = 'iso-2022-jp' ) }}
  • 167. Named parameters {{ content|convert_encoding('UTF-8', 'iso-2022-jp') }} which is the original charset and which one the target charset? {{ content|convert_encoding( from = 'UTF-8', to = 'iso-2022-jp' ) }} {{ content|convert_encoding( to = 'UTF-8', from = 'iso-2022-jp' ) }}
  • 168. Named parameters {{ content|convert_encoding('UTF-8', 'iso-2022-jp') }} PROPOSED FOR PHP which is the original charset and which one the target charset? {{ content|convert_encoding( from = 'UTF-8', to = 'iso-2022-jp' ) }} {{ content|convert_encoding( to = 'UTF-8', from = 'iso-2022-jp' ) }}
  • 169. Named parameters {{ include('template.html', {}, true, true) }} ! ! ! {{ include('template.html', ignore_missing = true) }} template variables with_context ignore_missing
  • 170. It's common to do things in batches 1 Image gallery 2 3 4 5 6
  • 171. The HTML of the image gallery <div class="row"> <div class="image"> ... </div> <div class="image"> ... </div> <div class="image"> ... </div> </div> ! <div class="row"> <div class="image"> ... </div> <div class="image"> ... </div> <div class="image"> ... </div> </div>
  • 172. The template without the "batch" filter {% for i, image in images %} {% if i is divisible by(3) %} <div class="row"> {% endif %} ! <div class="image"> <img src="" alt="" > <p>...</p> </div> ! {% if i is divisible by(3) %} </div> {% endif %} {% endfor %}
  • 173. The template without the "batch" filter {% for i, image in images %} {% if i is divisible by(3) %} <div class="row"> {% endif %} ! <div class="image"> <img src="" alt="" > <p>...</p> </div> ! {% if i is divisible by(3) %} </div> {% endif %} {% endfor %} UGLY CODE
  • 174. The template with the "batch" filter {% for row in images|batch(3) %} <div class="row"> ! {% for image in row %} <div class="image"> <img src="" alt="" > <p>...</p> </div> {% endfor %} ! </div> {% endfor %}
  • 175. The template with the "batch" filter {% for row in images|batch(3) %} <div class="row"> ! {% for image in row %} <div class="image"> <img src="" alt="" > <p>...</p> </div> {% endfor %} ! </div> {% endfor %}
  • 176. Short ternary operator $result = $condition ? 'is true';
  • 177. Short ternary operator $result = $condition ? 'is true'; ERROR Parse error: syntax error, unexpected ';' on line 1
  • 178. Short ternary operator $result = $condition ? 'is true'; ERROR Parse error: syntax error, unexpected ';' on line 1 {{ condition ? 'is true' }}
  • 179. Short ternary operator $result = $condition ? 'is true'; ERROR Parse error: syntax error, unexpected ';' on line 1 OK This works perfectly on Twig {{ condition ? 'is true' }}
  • 180. Short ternary operator <li class="{{ condition ? 'selected' }}"> ... </li> ! <li class="{{ condition ? 'selected' : '' }}"> ... </li>
  • 181. Short ternary operator <li class="{{ condition ? 'selected' }}"> ... </li> ! <li class="{{ condition ? 'selected' : '' }}"> ... </li> always use this
  • 182. Short slice syntax ! ! ! {{ user.friends[0:3] }} {{ user.friends[:-3] }} It combines array_slice, mb_substr and substr PHP functions. get first three friends get last three friends
  • 183. Short slice syntax {{ '0123456789'[0:] }} {# 0123456789 #} {{ '0123456789'[1:] }} {# 123456789 #} {{ '0123456789'[20:] }} {# (empty) #} {{ '0123456789'[-5:] }} {# 56789 #} {{ '0123456789'[-1:] }} {# 9 #} {{ '0123456789'[1:5] }} {# 12345 #} {{ '0123456789'[1:-5] }} {# 1234 #} OUTPUT
  • 184. The "loop" magic variable Everyone needs an $i variable inside the for loop. So Twig provides you this and other useful variables.
  • 185. The "loop" variable exists only inside the "for" {% for ... in collection %} {{ loop.index }} {{ loop.index0 }} {{ loop.first }} {{ loop.last }} {{ loop.length }} {% endfor %}
  • 186. The "loop" variable exists only inside the "for" {% for ... in collection %} {{ loop.index }} {{ loop.index0 }} {{ loop.first }} {{ loop.last }} {{ loop.length }} {% endfor %} 1, 2, 3, 4, 5, ... 0, 1, 2, 3, 4, ... true, false, false, ... ..., false, false, true 5
  • 187. {{ product.photo|image(400, 150, 0.9) }} The problem with filter arguments
  • 188. {{ product.photo|image(400, 150, 0.9) }} The problem with filter arguments What if I need to define more arguments?
  • 189. {{ product.photo|image(400, 150, 0.9) }} {{ product.photo|image( width = 400, height = 150, opacity = 0.9 ) }} The problem with filter arguments What if I need to define more arguments?
  • 190. {{ product.photo|image(400, 150, 0.9) }} {{ product.photo|image( width = 400, height = 150, opacity = 0.9 ) }} The problem with filter arguments What if I need to define more arguments? this is a valid solution for Twig, but the underlying PHP code is still very complex
  • 191. Defining a filter with lots or arguments $filter = new Twig_SimpleFilter('image', function ( $path, $width, $height, $opacity ) { $path = ... $width = ... $height = ... $opacity = ... });
  • 192. $filter = new Twig_SimpleFilter('image', function ( $path, $options = array() ) { $path = ... $width = $options['width']; $height = $options['height']; $opacity = $options['opacity']; }, array('is_variadic' => true)); Defining a variadic filter
  • 193. $filter = new Twig_SimpleFilter('image', function ( $path, $options = array() ) { $path = ... $width = $options['width']; $height = $options['height']; $opacity = $options['opacity']; }, array('is_variadic' => true)); Defining a variadic filter a single variadic parameter holds any number of passed parameters (unlimited)
  • 194. $filter = new Twig_SimpleFilter('image', function ( $path, $options = array() ) { $path = ... $width = $options['width']; $height = $options['height']; $opacity = $options['opacity']; }, array('is_variadic' => true)); ACCEPTED BY PHP Defining a variadic filter a single variadic parameter holds any number of passed parameters (unlimited)
  • 196. «Using Twig templates is the best decision Drupal ever made»
  • 197. Drupal 8 templates are safe, concise, modern and consistent. Twig
  • 198. Drupal 8 templates are safe, concise, modern and consistent.
  • 201. References • Official Twig documentation
 twig.sensiolabs.org/documentation
 • Twig in Drupal 8
 drupal.org/theme-guide/8/twig
  • 203. Contact info • javier.eguiluz@sensiolabs.com • github.com/javiereguiluz • linkedin.com/in/javiereguiluz ! ! !