+ All Categories
Home > Technology > Mastering Twig (DrupalCon Barcelona 2015)

Mastering Twig (DrupalCon Barcelona 2015)

Date post: 15-Apr-2017
Category:
Upload: javier-eguiluz
View: 5,105 times
Download: 2 times
Share this document with a friend
204
Javier Eguiluz September 22, 2015 Twig TRACK ROOM DATE SPEAKER Symfony 115 Mastering
Transcript
Page 1: Mastering Twig (DrupalCon Barcelona 2015)

Javier EguiluzSeptember 22, 2015

TwigTRACK ROOM DATE SPEAKERSymfony 115

Mastering

Page 2: Mastering Twig (DrupalCon Barcelona 2015)

License of this presentation

creativecommons.org/licenses/by-nc-sa/3.0

Page 3: Mastering Twig (DrupalCon Barcelona 2015)

ABOUT ME

Page 4: Mastering Twig (DrupalCon Barcelona 2015)

Javier EguiluzSymfony Evangelist

Page 5: Mastering Twig (DrupalCon Barcelona 2015)

ABOUT THIS TALK

Page 6: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 7: Mastering Twig (DrupalCon Barcelona 2015)

DRUPAL & TWIG

Page 8: Mastering Twig (DrupalCon Barcelona 2015)

Fast Easy to learn

Documented Concise Full featured

Extensible Tested Useful errors

Secure

Main Twig features

Page 9: Mastering Twig (DrupalCon Barcelona 2015)

My favorite feature

Consistent

Page 10: Mastering Twig (DrupalCon Barcelona 2015)

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!

Page 11: Mastering Twig (DrupalCon Barcelona 2015)

«Using Twig templatesis the best decisionDrupal ever made»

Page 12: Mastering Twig (DrupalCon Barcelona 2015)

DRUPAL 8

TWIG

Built-in Drupal templates use ~30% of the

available Twig features.

Page 13: Mastering Twig (DrupalCon Barcelona 2015)

AGENDA

Page 14: Mastering Twig (DrupalCon Barcelona 2015)

Defensive programming White spaces

Debug Escaping Reusing templates

Dates Dynamic templates

Cool features

Variables

Agenda

Page 15: Mastering Twig (DrupalCon Barcelona 2015)

ACCESSING VARIABLES

Page 16: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because you can easily improve the performance of your site/app.

Page 17: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 18: Mastering Twig (DrupalCon Barcelona 2015)

Accessing complex variables!

<nav>{{ content.links }}</nav>

core/themes/bartik/templates/comment.html.twig

Page 19: Mastering Twig (DrupalCon Barcelona 2015)

Accessing complex variables!

<nav>{{ content.links }}</nav>

core/themes/bartik/templates/comment.html.twig

Page 20: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 21: Mastering Twig (DrupalCon Barcelona 2015)

And what about performance?

Page 22: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 23: Mastering Twig (DrupalCon Barcelona 2015)

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

EXPECTEDPERFORMANCE

INCREASE

15%

Page 24: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 25: Mastering Twig (DrupalCon Barcelona 2015)

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') }}

Page 26: Mastering Twig (DrupalCon Barcelona 2015)

DEFENSIVE PROGRAMMING

Page 27: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because sooner or later errors will happen. What matters is how you deal with them.

Page 28: Mastering Twig (DrupalCon Barcelona 2015)

Dealing with undefined/empty variables

is empty defaultis defined

is null {% if %}

Page 29: Mastering Twig (DrupalCon Barcelona 2015)

The two recommended safeguards

{% if variable %} ... {% endif %} !

!

Hi {{ variable|default('user') }}

Page 30: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 31: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 32: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 33: Mastering Twig (DrupalCon Barcelona 2015)

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).

Page 34: Mastering Twig (DrupalCon Barcelona 2015)

Other safeguards available

{% if variable is null %} ... {% endif %} !

!

{% if variable is empty %} ... {% endif %} {% if variable is not empty %} ... {% endif %}

Page 35: Mastering Twig (DrupalCon Barcelona 2015)

Be ready when iterating empty collections

{% for item in collection %}

...

{% else %}

There are no items.

{% endfor %}

Page 36: Mastering Twig (DrupalCon Barcelona 2015)

Filter values before using them in the loop

{% for item in collection if item.published %}

...

{% else %}

There are no items.

{% endfor %}

Page 37: Mastering Twig (DrupalCon Barcelona 2015)

Avoid missing templates

{{ include('menu.twig') }}

This will always work because our theme will provide this template.

Page 38: Mastering Twig (DrupalCon Barcelona 2015)

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') }}

Page 39: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 40: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 41: Mastering Twig (DrupalCon Barcelona 2015)

Ignore missing templates{{ include('template.twig', ignore_missing = true) }} !

{{ source('template.twig', ignore_missing = true) }} !

{% embed 'template.twig' ignore missing %} ... {% endembed %}

Page 42: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 43: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 44: Mastering Twig (DrupalCon Barcelona 2015)

Declare filters as deprecatednew Twig_SimpleFilter('old_filter', ..., array(

'deprecated' => true,

'alternative' => 'new_filter'

));

Page 45: Mastering Twig (DrupalCon Barcelona 2015)

Declare filters as deprecatednew Twig_SimpleFilter('old_filter', ..., array(

'deprecated' => true,

'alternative' => 'new_filter'

));

These deprecations notices are not displayed or logged anywhere on Drupal yet.

NOTE

Page 46: Mastering Twig (DrupalCon Barcelona 2015)

Avoid missing blocks

{% if 'title' is block %}

<title>{{ block('title') }}<title>

{% endif %}

Page 47: Mastering Twig (DrupalCon Barcelona 2015)

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 %}

Page 48: Mastering Twig (DrupalCon Barcelona 2015)

WHITE SPACES

Page 49: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because it will make your templates more readable and it will save you time.

Page 50: Mastering Twig (DrupalCon Barcelona 2015)

<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

Page 51: Mastering Twig (DrupalCon Barcelona 2015)

<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

Page 52: Mastering Twig (DrupalCon Barcelona 2015)

<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

Page 53: Mastering Twig (DrupalCon Barcelona 2015)

<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

Page 54: Mastering Twig (DrupalCon Barcelona 2015)

<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

Page 55: Mastering Twig (DrupalCon Barcelona 2015)

<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

Page 56: Mastering Twig (DrupalCon Barcelona 2015)

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>

Page 57: Mastering Twig (DrupalCon Barcelona 2015)

Please, don't waste your time dealing with

white spaces.

Page 58: Mastering Twig (DrupalCon Barcelona 2015)

!

Twig templates should be readable

HTML pages should not!

Page 59: Mastering Twig (DrupalCon Barcelona 2015)

!

Twig templates should be readable

HTML pages should not

this is where you work everyday

!

browsers get a minimized and compressed HTML mess

Page 60: Mastering Twig (DrupalCon Barcelona 2015)

Sometimes you should add white spaces…

Page 61: Mastering Twig (DrupalCon Barcelona 2015)

White spaces around HTML attributes

<h2{{ title_attributes }}>{{ label }}</h2> core/modules/block/templates/block.html.twig

Page 62: Mastering Twig (DrupalCon Barcelona 2015)

White spaces around HTML attributes

<h2{{ title_attributes }}>{{ label }}</h2> core/modules/block/templates/block.html.twig

Page 63: Mastering Twig (DrupalCon Barcelona 2015)

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>

Page 64: Mastering Twig (DrupalCon Barcelona 2015)

Add white spaces to separate Twig & HTML

<h2 {{ title_attributes }}>{{ label }}</h2> !

!

<h2 class="..."> ... </h2>

<h2 > ... </h2>

Page 65: Mastering Twig (DrupalCon Barcelona 2015)

Add white spaces to separate Twig & HTML

<h2 {{ title_attributes }}>{{ label }}</h2> !

!

<h2 class="..."> ... </h2>

<h2 > ... </h2>

white space when the attributes are empty

Page 66: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 67: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 68: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 69: Mastering Twig (DrupalCon Barcelona 2015)

Hiding HTML code inside Twig strings<div id="site-name"{{ hide_name ? ' class="hidden"' }}> !

!

!

!

!

<div id="site-name">

<div id="site-name" class="hidden">

WARNINGHTML attributes

defined in Twig strings are easy to overlook

core/themes/bartik/templates/maintenance-page.html.twig

Page 70: Mastering Twig (DrupalCon Barcelona 2015)

Hiding HTML code inside Twig strings<div id="site-name"{{ hide_name ? ' class="hidden"' }}> !

!

!

!

!

<div id="site-name">

<div id="site-name" class="hidden">

WARNINGHTML attributes

defined in Twig strings are easy to overlook

core/themes/bartik/templates/maintenance-page.html.twig

DANGERIf you miss this single white space, the page

design breaks

Page 71: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 72: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 73: Mastering Twig (DrupalCon Barcelona 2015)

DEBUG

Page 74: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because it will save you a lot of time while developing your templates.

Page 75: Mastering Twig (DrupalCon Barcelona 2015)

Configure Twig behavior

# sites/default/services.yml parameters: twig.config: debug: true auto_reload: null cache: true

Page 76: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 77: Mastering Twig (DrupalCon Barcelona 2015)

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"> !<!-- ... -->

Page 78: Mastering Twig (DrupalCon Barcelona 2015)

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=/node" method="post" id="user-login-form" accept-charset="UTF-8">

Page 79: Mastering Twig (DrupalCon Barcelona 2015)

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' -->

Page 80: Mastering Twig (DrupalCon Barcelona 2015)

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…

Page 81: Mastering Twig (DrupalCon Barcelona 2015)

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…

Page 82: Mastering Twig (DrupalCon Barcelona 2015)

Which variables are passed to the template?

Built-in templates include comments with the full list of variables passed to the Twig template.

Page 83: Mastering Twig (DrupalCon Barcelona 2015)

Easier way to introspect all variables

<pre>

{{ dump() }}

</pre>

Page 84: Mastering Twig (DrupalCon Barcelona 2015)

Easier way to introspect all variables

<pre>

{{ dump() }}

</pre>

It dumps the contents of all the variables

defined in the template.

Page 85: Mastering Twig (DrupalCon Barcelona 2015)

It's better to dump just the variables you need

<pre> {{ dump(label, title_attributes) }}

</pre>

Page 86: Mastering Twig (DrupalCon Barcelona 2015)

It's better to dump just the variables you need

<pre> {{ dump(label, title_attributes) }}

</pre>

It dumps only the given variables

Page 87: Mastering Twig (DrupalCon Barcelona 2015)

CAUTION!

Don't forget to rebuild your cache after changing the config files and templates.

Page 88: Mastering Twig (DrupalCon Barcelona 2015)

drupalconsole.com

Page 89: Mastering Twig (DrupalCon Barcelona 2015)

drupalconsole.com

$ drupal cache:rebuild $ drupal c:r

Page 90: Mastering Twig (DrupalCon Barcelona 2015)

ESCAPING

Page 91: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because it can prevent you a lot of security-related problems.

Page 92: Mastering Twig (DrupalCon Barcelona 2015)

CAUTION!

Drupal has replaced the default Twig escaping filter by their own.

Page 93: Mastering Twig (DrupalCon Barcelona 2015)

By default, contents are escaped for HTML

Hi {{ content }}!

$content = '<strong>John</strong>';

Page 94: Mastering Twig (DrupalCon Barcelona 2015)

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>!

Page 95: Mastering Twig (DrupalCon Barcelona 2015)

The "raw" filter prevents the escaping

Hi {{ content|raw }}!

$content = '<strong>John</strong>';

Page 96: Mastering Twig (DrupalCon Barcelona 2015)

The "raw" filter prevents the escaping

Hi {{ content|raw }}!

$content = '<strong>John</strong>';

What you expect…

Hi John!

What you get…

Hi John!

Page 97: Mastering Twig (DrupalCon Barcelona 2015)

What if contents are used in URLs or JS?

<a href="...?param={{ value }}"></a> !

!

!

<script> var variable = "{{ content }}"; </script>

Page 98: Mastering Twig (DrupalCon Barcelona 2015)

What if contents are used in URLs or JS?

<a href="...?param={{ value }}"></a> !

!

!

<script> var variable = "{{ content }}"; </script>

WRONG HTML ESCAPING

Page 99: Mastering Twig (DrupalCon Barcelona 2015)

Applying different escaping strategies

<a href="...?param={{ value|e('url') }}"></a> !

!

!

<script> var variable = "{{ content|e('js') }}"; </script>

Page 100: Mastering Twig (DrupalCon Barcelona 2015)

Escaping strategies available in Twig

{{ content|e('html') }}

{{ content|e('js') }}

{{ content|e('css') }}

{{ content|e('url') }}

{{ content|e('html_attr') }}

Page 101: Mastering Twig (DrupalCon Barcelona 2015)

REUSING TEMPLATES

Page 102: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because it allows you to avoid repeating code and it makes your themes easier to maintain.

Page 103: Mastering Twig (DrupalCon Barcelona 2015)

Lots of different ways to reuse templates

{% embed %} {% extends %}include()

{% set %} {% use %}macro()

Page 104: Mastering Twig (DrupalCon Barcelona 2015)

How often are these alternatives used

{% embed %}

{% extends %} include( )

{% set %} {% use %}

macro( )

Always

Sometimes

Rarely

Page 105: Mastering Twig (DrupalCon Barcelona 2015)

Lots of different ways to reuse templates

{% embed %} {% extends %}include()

{% set %} {% use %}macro()

Page 106: Mastering Twig (DrupalCon Barcelona 2015)

Use {% extends %} to share layouts

Page 107: Mastering Twig (DrupalCon Barcelona 2015)

Use {% extends %} to share layouts

1 layout with the common design elements

Page 108: Mastering Twig (DrupalCon Barcelona 2015)

Use {% extends %} to share layouts

1 layout with the common design elements

+

4 simple pages which only define their contents

Page 109: Mastering Twig (DrupalCon Barcelona 2015)

layout.twig<!DOCTYPE html> <html> <head> <title> {% block title %}ACME website{% endblock %} </title> </head> <body> <div class="container"> {% block content %}{% endblock %} </div> </body> </html>

Page 110: Mastering Twig (DrupalCon Barcelona 2015)

layout.twig<!DOCTYPE html> <html> <head> <title> {% block title %}ACME website{% endblock %} </title> </head> <body> <div class="container"> {% block content %}{% endblock %} </div> </body> </html>

Page 111: Mastering Twig (DrupalCon Barcelona 2015)

Other templates can reuse this layout{% extends 'layout.twig' %}

!

{% block title %}Community{% endblock %}

{% block content %}

<div> ... </div>

{% endblock %}

Page 112: Mastering Twig (DrupalCon Barcelona 2015)

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.twigextends layout.twig extends schedule.twig

Page 113: Mastering Twig (DrupalCon Barcelona 2015)

Lots of different ways to reuse templates

{% embed %} {% extends %}include()

{% set %} {% use %}macro()

Page 114: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 115: Mastering Twig (DrupalCon Barcelona 2015)

When should you use include( )• To reuse large fragments of code, such as sidebars,

navigation menus, etc.

Page 116: Mastering Twig (DrupalCon Barcelona 2015)

Lots of different ways to reuse templates

{% embed %} {% extends %}include()

{% set %} {% use %}macro()

Page 117: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 118: Mastering Twig (DrupalCon Barcelona 2015)

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 %}

Page 119: Mastering Twig (DrupalCon Barcelona 2015)

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>

Page 120: Mastering Twig (DrupalCon Barcelona 2015)

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)

Page 121: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 122: Mastering Twig (DrupalCon Barcelona 2015)

Lots of different ways to reuse templates

{% embed %} {% extends %}include()

{% set %} {% use %}macro()

Page 123: Mastering Twig (DrupalCon Barcelona 2015)

Grid-based design

embed allows to reuse inner page structures (e.g. the 3-column grid)

Page 124: Mastering Twig (DrupalCon Barcelona 2015)

Grid-based design

embed allows to reuse inner page structures (e.g. the 3-column grid)

Page 125: Mastering Twig (DrupalCon Barcelona 2015)

You can't use "include" to solve this problem

{{ include('common/grid_3.twig') }}

Page 126: Mastering Twig (DrupalCon Barcelona 2015)

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)

Page 127: Mastering Twig (DrupalCon Barcelona 2015)

You can't use "extends" to solve this problem

{% extends 'common/grid_3.twig' %}

!

{% block column1 %} ... {% endblock %}

{% block column2 %} ... {% endblock %}

{% block column3 %} ... {% endblock %}

Page 128: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 129: Mastering Twig (DrupalCon Barcelona 2015)

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>

Page 130: Mastering Twig (DrupalCon Barcelona 2015)

Reuse the three-column grid template{% embed 'common/grid_3.twig' %} {% block column1 %} ... contents ... {% endblock %} !

{% block column2 %} ... contents ... {% endblock %} !

{% block column3 %} ... contents ... {% endblock %} {% endembed %}

Page 131: Mastering Twig (DrupalCon Barcelona 2015)

When should you use {% embed %}• To reuse page structures across different templates

(e.g. grids)

Page 132: Mastering Twig (DrupalCon Barcelona 2015)

Lots of different ways to reuse templates

{% embed %} {% extends %}include()

{% set %} {% use %}macro()

Page 133: Mastering Twig (DrupalCon Barcelona 2015)

Reusing fragments with {% set %}

{% set navigation %} <a href="...">Previous</a> ... ... <a href="...">Next</a> {% endset %}

Page 134: Mastering Twig (DrupalCon Barcelona 2015)

Reusing fragments with {% set %}

{% set navigation %} <a href="...">Previous</a> ... ... <a href="...">Next</a> {% endset %} {{ navigation }}

{{ navigation }}

Page 135: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 136: Mastering Twig (DrupalCon Barcelona 2015)

Lots of different ways to reuse templates

{% embed %} {% extends %}include()

{% set %} {% use %}macro()

Page 137: Mastering Twig (DrupalCon Barcelona 2015)

When should you use {% use %}• This is too advanced and for very specific use

cases. • You should probably never use it when creating

themes.

Page 138: Mastering Twig (DrupalCon Barcelona 2015)

DYNAMIC TEMPLATES

Page 139: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because Drupal allows to create sites with very advanced needs.

Page 140: Mastering Twig (DrupalCon Barcelona 2015)

Templates created on-the-fly

{% set code = 'Hi {{ name }}' %}

{% set template = template_from_string(code) %}

!

{{ include(template) }}

Page 141: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 142: Mastering Twig (DrupalCon Barcelona 2015)

Templates created on-the-fly

{{ include(template_from_string(

'Hi {{ name }}'

)) }}

Page 143: Mastering Twig (DrupalCon Barcelona 2015)

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) }}

Page 144: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 145: Mastering Twig (DrupalCon Barcelona 2015)

Imagine a site which allows this customization

Section 1

Section 2

Default design

Page 146: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 147: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 148: Mastering Twig (DrupalCon Barcelona 2015)

Twig Sandbox in practice{% sandbox %}

{{ include(section.name ~ '/sidebar.twig') }}

{% endsandbox %}

!

!

{{ include(section.name ~ '/sidebar.twig', sandboxed = true) }}

Page 149: Mastering Twig (DrupalCon Barcelona 2015)

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.

Page 150: Mastering Twig (DrupalCon Barcelona 2015)

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 );

Page 151: Mastering Twig (DrupalCon Barcelona 2015)

DATES

Page 152: Mastering Twig (DrupalCon Barcelona 2015)

WHY IS THIS IMPORTANT??

Because dealing with dates is not easy and Twig can perform a lot of operations on dates.

Page 153: Mastering Twig (DrupalCon Barcelona 2015)

Timezones support

{{ 'now'|date(timezone='Asia/Tokyo') }} !

{{ 'now'|date(timezone=user.timezone) }}

Page 154: Mastering Twig (DrupalCon Barcelona 2015)

Comparing dates

{% if event.startsAt > date('now') %}

Buy tickets

{% endif %}

Page 155: Mastering Twig (DrupalCon Barcelona 2015)

Comparing dates

{% if event.startsAt > date('now') %}

Buy tickets

{% endif %} NOTE This is the date( )

function, not the date filter

Page 156: Mastering Twig (DrupalCon Barcelona 2015)

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 }}

Page 157: Mastering Twig (DrupalCon Barcelona 2015)

COOL FEATURES

Page 158: Mastering Twig (DrupalCon Barcelona 2015)

These are some of the features that put Twig years ahead of PHP

Page 159: Mastering Twig (DrupalCon Barcelona 2015)

Useful filters for collections

{{ user.friends|first }}

{{ event.sessions|last }}

Page 160: Mastering Twig (DrupalCon Barcelona 2015)

Useful tests for strings

{% if url starts with 'https://' %}

{% endif %}

!

{% if file_path ends with '.pdf' %}

{% endif %}

!

{% if phone matches '/^[\\d\\.]+$/' %}

{% endif %}

Page 161: Mastering Twig (DrupalCon Barcelona 2015)

REJECTED BY

PHPUseful tests for strings

{% if url starts with 'https://' %}

{% endif %}

!

{% if file_path ends with '.pdf' %}

{% endif %}

!

{% if phone matches '/^[\\d\\.]+$/' %}

{% endif %}

Page 162: Mastering Twig (DrupalCon Barcelona 2015)

The "in" operator

{% if password in username %}

BAD PASSWORD

{% endif %}

!

{% if method in ['GET', 'POST'] %}

...

{% endif %}

Page 163: Mastering Twig (DrupalCon Barcelona 2015)

The "in" operator

{% if password in username %}

BAD PASSWORD

{% endif %}

!

{% if method in ['GET', 'POST'] %}

...

{% endif %}

REJECTED BY

PHP

Page 164: Mastering Twig (DrupalCon Barcelona 2015)

Named parameters{{ content|convert_encoding('UTF-8', 'iso-2022-jp') }}

Page 165: Mastering Twig (DrupalCon Barcelona 2015)

Named parameters{{ content|convert_encoding('UTF-8', 'iso-2022-jp') }}

which is the original charset and which one the target charset?

Page 166: Mastering Twig (DrupalCon Barcelona 2015)

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' ) }}

Page 167: Mastering Twig (DrupalCon Barcelona 2015)

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' ) }}

Page 168: Mastering Twig (DrupalCon Barcelona 2015)

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' ) }}

Page 169: Mastering Twig (DrupalCon Barcelona 2015)

Named parameters{{ include('template.html', {}, true, true) }} !

!

!

{{ include('template.html', ignore_missing = true) }}

template variables

with_context

ignore_missing

Page 170: Mastering Twig (DrupalCon Barcelona 2015)

It's common to do things in batches

1

Image gallery

2 3

4 5 6

Page 171: Mastering Twig (DrupalCon Barcelona 2015)

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>

Page 172: Mastering Twig (DrupalCon Barcelona 2015)

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 %}

Page 173: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 174: Mastering Twig (DrupalCon Barcelona 2015)

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 %}

Page 175: Mastering Twig (DrupalCon Barcelona 2015)

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 %}

Page 176: Mastering Twig (DrupalCon Barcelona 2015)

Short ternary operator

$result = $condition ? 'is true';

Page 177: Mastering Twig (DrupalCon Barcelona 2015)

Short ternary operator

$result = $condition ? 'is true';

ERROR Parse error: syntax error, unexpected ';' on line 1

Page 178: Mastering Twig (DrupalCon Barcelona 2015)

Short ternary operator

$result = $condition ? 'is true';

ERROR Parse error: syntax error, unexpected ';' on line 1

{{ condition ? 'is true' }}

Page 179: Mastering Twig (DrupalCon Barcelona 2015)

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' }}

Page 180: Mastering Twig (DrupalCon Barcelona 2015)

Short ternary operator

<li class="{{ condition ? 'selected' }}">

...

</li>

!

<li class="{{ condition ? 'selected' : '' }}"> ...

</li>

Page 181: Mastering Twig (DrupalCon Barcelona 2015)

Short ternary operator

<li class="{{ condition ? 'selected' }}">

...

</li>

!

<li class="{{ condition ? 'selected' : '' }}"> ...

</li>

always use this

Page 182: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 183: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 184: Mastering Twig (DrupalCon Barcelona 2015)

The "loop" magic variable

Everyone needs an $i variable inside the for loop. So Twig provides you this and other useful variables.

Page 185: Mastering Twig (DrupalCon Barcelona 2015)

The "loop" variable exists only inside the "for"

{% for ... in collection %}

{{ loop.index }}

{{ loop.index0 }}

{{ loop.first }}

{{ loop.last }}

{{ loop.length }}

{% endfor %}

Page 186: Mastering Twig (DrupalCon Barcelona 2015)

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

Page 187: Mastering Twig (DrupalCon Barcelona 2015)

{{ product.photo|image(400, 150, 0.9) }}

The problem with filter arguments

Page 188: Mastering Twig (DrupalCon Barcelona 2015)

{{ product.photo|image(400, 150, 0.9) }}

The problem with filter arguments

What if I need to define more arguments?

Page 189: Mastering Twig (DrupalCon Barcelona 2015)

{{ 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?

Page 190: Mastering Twig (DrupalCon Barcelona 2015)

{{ 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

Page 191: Mastering Twig (DrupalCon Barcelona 2015)

Defining a filter with lots or arguments$filter = new Twig_SimpleFilter('image', function (

$path, $width, $height, $opacity

) {

$path = ...

$width = ...

$height = ...

$opacity = ...

});

Page 192: Mastering Twig (DrupalCon Barcelona 2015)

$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

Page 193: Mastering Twig (DrupalCon Barcelona 2015)

$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)

Page 194: Mastering Twig (DrupalCon Barcelona 2015)

$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)

Page 195: Mastering Twig (DrupalCon Barcelona 2015)

TO SUM UP

Page 196: Mastering Twig (DrupalCon Barcelona 2015)

«Using Twig templates is the best decision Drupal ever made»

Page 197: Mastering Twig (DrupalCon Barcelona 2015)

Drupal 8 templates are safe, concise, modern

and consistent.

Twig

Page 198: Mastering Twig (DrupalCon Barcelona 2015)

Drupal 8 templates are safe, concise, modern

and consistent.

Page 199: Mastering Twig (DrupalCon Barcelona 2015)

Drupal 8 themes

Page 200: Mastering Twig (DrupalCon Barcelona 2015)

REFERENCES

Page 201: Mastering Twig (DrupalCon Barcelona 2015)

References• Official Twig documentation

twig.sensiolabs.org/documentation

• Twig in Drupal 8drupal.org/theme-guide/8/twig

Page 202: Mastering Twig (DrupalCon Barcelona 2015)

CONTACT

Page 203: Mastering Twig (DrupalCon Barcelona 2015)

Contact info• [email protected] • github.com/javiereguiluz • linkedin.com/in/javiereguiluz

!

!

!

Page 204: Mastering Twig (DrupalCon Barcelona 2015)

Recommended