Date post: | 28-Aug-2014 |
Category: |
Software |
Upload: | brandonkelly212 |
View: | 665 times |
Download: | 5 times |
Making Sense of Twig
Brandon Kelly
What is Twig?
It’s a templating language. !
All templating features are available globally to all templates in any context, as part of the language. !
It’s not up to each and every application feature to provide its own tags.
It’s super powerful. !
- Many ways to stay DRY - Custom variables - Functions - Filters - It knows math - Whitespace control - Extensible
Templates get compiled into PHP, so it’s super fast.
Twig even makes debugging a piece of cake.
Used by some popular apps: !
- LemonStand - Symfony - Craft - others
twig.sensiolabs.org
The Language
Types of Tags
<html> <head> <title>{{ title }}</title> </head> <body> <h1>{{ title }}</h1> {% block main %} <p>Hey!</p> {% endblock %} </body> </html>
<html> <head> <title>{{ title }}</title> </head> <body> <h1>{{ title }}</h1> {% block main %} <p>Hey!</p> {% endblock %} </body> </html>
<html> <head> <title>{{ title }}</title> </head> <body> <h1>{{ title }}</h1> {% block main %} <p>Hey!</p> {% endblock %} </body> </html>
Twig code always lives within one of these three tag pairs: !
{# ... #} {{ ... }} {% ... %}
{# ... #}
Comment Tags
Comment tags are like HTML comments, except they won’t show up in the page source. !
{# This won’t make it to the page source #} !
<!-- This will -->
{{ ... }}
Output Tags
Output tags output stuff to the browser. !
{{ title }} !
{{ "Howdy" }} !
{{ 84 / 2 }}
{% ... %}
Logic Tags
Logic tags (or just “tags”) control the logic of the template: !
- Conditionals - Loops - Variable definitions - Macros - Template includes - etc.
The syntax varies from tag to tag.
Some are just a single word. !
{% requireLogin %}
Some take parameters. !
{% exit 404 %}
Some have a closing tag. !
{% block content %} <p>Hey</p> {% endblock %}
Some even have nested tags. !
{% if foo %} <p>Something</p> {% else %} <p>Something else</p> {% endif %}
It really all depends on the tag. !
The only thing they have in common is that they all start with “{%”, followed by the tag name. !
{% tagname ...
Twig comes with several built-in tags. !{% autoescape %} {% block %} {% filter %} {% do %} {% embed %} {% extends %} {% autoescape %} {% flush %} {% for %} {% from %}
{% if %} {% import %} {% include %} {% macro %} {% sandbox %} {% set %} {% spaceless %} {% use %} {% verbatim %}
{% autoescape %} !
Escapes text for HTML. !
{% autoescape %} <p>Hey</p> {% endautoescape %} => <p>Hey</p>
{% autoescape %} (Cont.) !
...or Javascript !
{% autoescape "js" %} http://foo.com {% endautoescape %} => http\x3A\x2F\x2Ffoo.com
{% spaceless %} !
Removes any whitespace between Twig/HTML tags. !
{%- spaceless %} <p>Hey there</p> {% endspaceless -%} => <p>Hey there</p>
{% verbatim %} !
Defines template code that Twig should output as-is, without parsing. !
{% verbatim %} <p>Type “{{ foo }}”.</p> {% endverbatim %} => <p>Type “{{ foo }}”.</p>
You never put a Twig tag inside another Twig tag. !
Bad: !
{{ "Hey {{ firstName }}" }} !
Good: !
{{ "Hey " ~ firstName }}
Values, Expressions, and Variables
Twig supports five different types of values. !
Strings: "Hey" / 'Hey' Numbers: 42 / 3.14 Booleans: true / false Arrays: ['a', 'b', 'c'] Objects: { foo: 'bar' }
An expression is either a solo value, or multiple values combined to form another value. !
"Hey" => "Hey" "Hey "~"there" => "Hey there" 10 => 10 true => true ['a','b','c'] => ['a','b','c'] "Day "~1 => "Day 1" 10*(5+5) => 100
Variables are values that get set to a name, to be referenced later. !
Use the {% set %} tag to create them. !
{% set foo = "foo" %} {% set a = 42 %} {% set foobar = foo~"bar" %}
The {% set %} tag can also be used as a tag pair. !
{% set foo %} <p>foo</p> {% endset %} !
That’s the same as: !
{% set foo = "\n <p>foo</p>\n " %}
Variables are one tool for keeping our templates DRY. !
{% set title = "About Us" %} ... <title>{{ title }}</title> ... <h1>{{ title }}</h1>
Filters
Filters modify values. !
They can uppercase text, merge arrays, and lots of other things.
To pass a value through a filter, type a pipe (“|”) after the value, followed by the filter name. !
"foo"|upper => "FOO" 21.3|round => 21 ['a','b','c']|length => 3
Some accept arguments. !
"foobar"|slice(0,3) => foo
You can even chain them. !
"foobar"|slice(0,3)|upper => FOO
You can add filters to variables, too. !
{% set foo = "foo" %} {{ foo|length }} => 3
You can use them in pretty much any context you can think of. !
{{ value|filter }} !
{% set foo = value|filter %} !
{% do func(value|filter) %}
Filters only modify the value directly before the filter. !
"foo"~"bar"|upper => fooBAR !
("foo"~"bar")|upper => FOOBAR
Twig comes with tons of built-in filters: |abs |batch |capitalize |covert_encoding |date |date_modify |default |escape |first |format |join |json_encode |keys |last |length |lower
|nl2br |number_format |merge |upper |raw |replace |reverse |round |slice |sort |split |striptags |title |trim |url_encode
|length !
Finds the length of a string or array. !
{{ "foobar"|length }} => 6 !
{{ [1, 2, 3]|length }} => 3
|upper & |lower !
Modify the casing of a string. !
{{ "foo"|upper }} => FOO !
{{ "BAR"|lower }} => bar
|raw !
Protects a string from getting escaped. !
{{ "<p>Hey</p>" }} => <p>Hey</> !
{{ "<p>Hey</p>"|raw }} => <p>Hey</p>
|date !
Formats a date. !
{{ now|date("F j, Y") }} => April 23, 2014
Functions
Functions perform functions. !
To call one, type its name, followed by parentheses. !
{{ parent() }}
Many functions accept arguments: !
random(1, 10)
Some of them are global: !
{{ dump(foo) }} !
And they can also be nested within objects: !
{{ craft.isLocalized() }}
Twig comes with a few global functions built-in:
attribute() block() constant() cycle() date() dump() include()
max() min() parent() random() range() source() template_from_string()
min() & max() !
Returns the smallest/largest value in a given array. !
{{ min([1, 2, 3]) }} => 1 !
{{ max([1, 2, 3]) }} => 3
random() !
{{ random("foobar") }} => f/o/b/a/r !
{{ random([1, 2, 3]) }} => 1/2/3 !
{{ random(10) }} => 1/2/3/4/5/6/7/8/9/10
range() !
Creates a range of numbers as an array. !
{% set r = range(1, 5) %} => [1, 2, 3, 4, 5] !
{% set r = [1..5] %} => [1, 2, 3, 4, 5]
dump() !
Outputs information about a given variable. Helpful when debugging. !
{{ dump(foo) }} !
(In Craft, dump() is only available in Dev Mode.)
Conditionals
You can prevent certain parts of your template from executing unless a certain condition is met by wrapping it in conditional tags.
Conditionals always open with an {% if %} tag, and close with an {% endif %} tag. !
{% if user %} <p>Hey there handsome!</p> {% endif %}
You can also specify template code to be executed if the condition doesn’t pass, using the {% else %} tag. !
{% if user %} <p>Hey there handsome!</p> {% else %} <p>Have we met?</p> {% endif %}
There’s also an {% elseif %} tag if you need fallback conditions. !
{% if user %} <p>Hey there handsome!</p> {% elseif username %} <p>Is this {{ username }}?</p> {% else %} <p>Have we met?</p> {% endif %}
Conditionals can be nested. !
{% if user %} {% if user.male %} <p>Hey there handsome!</p> {% else %} <p>Hey pretty lady!</p> {% endif %} {% elseif username %} <p>Is this {{ username }}?</p> {% else %} <p>Have we met?</p> {% endif %}
A single condition can be made up of multiple expressions joined together with “and” or “or”. !
{% if foo and bar %} !
{% if foo or bar %}
You can negate a condition by typing “not” before it. !
{% if not foo %}
You can also group expressions together using parentheses. !
{% if foo and ( foo == "foo" or foo == "bar" ) %}
Tests
Tests are little conditional helpers. They don’t have to be used within conditionals, but usually are.
To write a test, add either “is” or “is not” after a variable, followed by the test name. !
{% if foo is defined %} !
{% if foo is not empty %}
Twig comes with a few tests built-in. !
is constant is defined is divisible by is empty is even
is iterable is null is odd is same as
is defined !
Tests whether a variable exists at all, without Twig getting mad at you if it’s not. !
{% if foo is defined %}
is divisible by !
Tests whether a number is divisible by another. !
{% if 5 is divisible by(2) %} !
That’s similar to writing: !
{% if 5 % 2 == 0 %}
is even & is odd !
Tests whether a number is even/odd. !
{% if 5 is even %} !
{% if 5 is odd %}
is empty !
Tests whether a variable is “empty”. !
{% if foo is not empty %} !
That’s the same as writing: !
{% if foo %}
is same as !
Tests whether two variables have the same *type*. !
{% if 5 == "5" %} => true !
{% if 5 is same as "5" %} => false
Working with Arrays and Objects
Arrays and objects both contain multiple values.
Arrays contain values in a specific order, and have an inherent numerical index. !
{% set arr = ['a','b','c'] %} !
{{ arr[0] }} => 'a' {{ arr[1] }} => 'b' {{ arr[2] }} => 'c'
Objects contain key-value pairs, and the order is generally less important. !
{% set obj = { foo: "Foo", bar: "Bar" } %} !
{{ obj.foo }} => "Foo" {{ obj.bar }} => "Bar"
You can merge arrays together with the ‘merge’ filter. !
{% set a1 = ['a','b'] %} {% set a2 = ['c','d'] %} {% set a3 = a1|merge(a2) %} !
=> ['a','b','c','d']
You can merge objects together too. !
{% set o1 = {foo: "Foo"} %} {% set o2 = {bar: "Bar"} %} {% set o3 = o1|merge(o2) %} !
=> {foo: "Foo", bar: "Bar"}
You can grab part of an array with the “slice” filter. !
{% set a1 = ['a','b','c'] %} {% set a2 = a1|slice(0, 2) %} !
=> ['a','b'] !
A shortcut is also available: !
{% set a2 = a1[0:2] %}
Looping througharrays and objects
You can loop through arrays with the {% for %} tag. !
The syntax is: !
{% for itemname in myarray %} ... {% endfor %} !
The 2nd param (“itemname”) is whatever you want to call each item within the array.
Example 1: Age field !
<select name="age"> {% for age in [0..150] %} <option> {{ age }}</option> {% endfor %} </select>
Example 2: Exp. Year field !
{% set y1 = now.year %} {% set y2 = y1 + 10 %} !
<select name="exp_year"> {% for year in [y1..y2] %} <option> {{ year }}</option> {% endfor %} </select>
Example 3: Navigation !
{% set nav = [ { title: "Home", uri: "" }, { title: "About", uri: "about" } ] %} !
<nav> {% for item in nav %} <a href="{{ url(item.uri) }}"> {{ item.title }}</a> {% endfor %} </nav>
DRY Templating
Includes
Templates can include others.
Include Template
Parent Template
Parent Template
Use the {% include %} tag to include another template wherever the tag is placed. !
{% include "some/template" %}
By default, any variables available to the “parent” template will also be available in the included template. !
{% set foo = "foo" %} {% include "myinclude" %} !
myinclude.html: {{ foo }} => foo
You can define additional variables just for the included template using the “with” param. !
{% include "myinclude" with { foo: "foo" } %} !
some/template.html: {{ foo }} => foo
To *only* pass certain variables to the included template, use the “only” param. !
{% set foo = "foo" %} {% include "myinclude" with { bar: "bar" } only %} !
some/template.html: {% if foo is defined %} => false
Extending Templates
A template can extend another, overriding parts of it (called “blocks”).
Parent Template
Block
Child Template Child Template
Define overridable areas in the parent template using the {% block %} tag. !
<body> {% block body %} <p>Default content</p> {% endblock %} </body>
Use the {% extends %} tag within your child template to tell it which template it extends. !
{% extends "layout" %}
Override the parent’s blocks by creating new blocks in the child template with the same name. !
{% extends "layout" %} !
{% block body %} <p>Override content</p> {% endblock %}
You can output the parent block’s content using the parent() function. !
{% extends "layout" %} !
{% block body %} {{ parent() }} <p>Additional content</p> {% endblock %}
Multi-level inheritance is totally possible.
Embedding Templates
Twig also lets you “embed” other templates, which is similar to including them, except you get to override the child template’s blocks in the process.
Use the {% embed %} tag to embed another template. !
{% embed "promo" %} {% block body %} {{ parent() }} <span class="ribbon"> 50% off! </span> {% endblock %} {% endembed %}
Macros
Macros are like includes that are defined right within another template.
Use the {% macro %} tag to define them. !
{% macro errors(list) %} {% if list|length %} <ul class="errors"> {% for error in list %} <li>{{ error }}</li> {% endfor %} </ul> {% endif %} {% endmacro %}
You must import macros before you can use them, with either {% from %} or {% import %}. !
{% from _self import errors %} {{ errors(entry.allErrors) }} !
!
{% import _self as m %} {{ m.errors(entry.allErrors) }}
You can import macros from other templates, too. !
{% from "macros" import errors %} {{ errors(entry.allErrors) }} !
!
{% import "macros" as m %} {{ m.errors(entry.allErrors) }} !
The End.