{# comments #}
{{ output }}
{% tags %}
'single quotes'
"double quotes"
~
is the string concatination operator: 'foo'~'bar' => 'foobar'
1, 2.5, -7
true
and false
['foo', 23, []]
{key: 'value', anotherKey: [1, 2, 'frogs']}
+, -, and, or, not, ==, <, >,
etc.) plus filters and functions.['a', 'b']|join(', ')
'a, b'
['a', 'b']|join(', ')|upper
'A, B'
max(foo, bar, 0)
Filters: currency, datetime, filesize, filter, group, indexOf, intersect, lcfirst, markdown (md), number, parseRefs, percentage, replace, translate (t), ucfirst, ucwords, without
Functions: ceil, floor, getCsrfInput, getFootHtml, getHeadHtml, shuffle, url
Tags: cache, exit, header, includeCss, includeCssFile, includeHiResCss, includeJs, includeJsFile, nav, paginate, redirect, requireLogin, requirePermission, switch
{{ value }}
Convert value
to a string
Print that string out inline
{{ 'hello, world'|upper }}
HELLO, WORLD
{{ dump(foo) }}
All the details (var_dump
) about foo, printed out.
Need to have devMode
enabled for this to work.
{{dump([1, 2, 'fish'])}}
array(3) {
[0]=> int(1)
[1]=> int(2)
[2]=> string(4) "fish"
}
{% set foo = bar %}
foo
will have whatever type bar
has
{% set foo %}
...bunch of template code...
{% endset %}
foo
will be a string
{% if someValue %}
...
{% elseif someOtherValue %}
...
{% else %}
...
{% endif %}
Things that are false: false
, 0
, ''
, []
Things that are true: everything else
{% switch matrixBlock.type %}
{% case 'text' %}
... matrixBlock.type is text ...
{% case 'image' %}
... matrixBlock.type is image ...
{% default %}
... all other cases ...
{% endswitch %}
At most one case block is evaluated.
no fall-through, no break
{% for foo in arrayLikeThing %}
... do something with {{foo}} ...
{% endfor %}
foo
is only defined inside the for
loop.
Traversable
interface.
(e.g. ElementCriteriaModel
)
['thingOne', 'thingTwo', 3.14159]
'a'..'z'
{ key: value, key2: value2 }
{% for animal in ['cat', 'dog', 'fish'] %}
all about the {{animal}}
{% endfor %}
all about the cat
all about the dog
all about the fish
{% set pets = {
cat: 'Puff',
dog: 'Spot'
} %}
{% for animal in pets %}
{{loop.index}}: {{animal}}
{% endfor %}
1: Puff
2: Spot
{% for animal in pets|keys %}
{{loop.index0}}: {{animal}}
{% endfor %}
0: cat
1: dog
{% for key, value in pets %}
{{key}}: {{value}}
{% endfor %}
cat: Puff
dog: Spot
loop.index
and loop.index0
loop.first
and loop.last
loop.revindex
and loop.revindex0
loop.length
loop.parent
loop.parent.loop.index
)
{% macro foo() %}
...
{% endmacro %}
defined in the same file
{% import _self as self %}
defined in a different file
{% import '_macros/_utils' as m_utils %}
{{ m_utils.foo() }}
{% set ourFoo = m_utils.foo() %}
{% macro richText(t) %}
{# typogrify filter for smart quotes #}
{{t|smartypants}}
{% endmacro %}
{{ m_utils.richText(entry.body) }}
{{ m_utils.richText() }} {# oops! #}
{% macro richText(t) %}
{% if t|default('') %}
{{t|smartypants}}
{% endif %}
{% endmacro %}
What if we want more classes on that div
?
{{ m_utils.richText(entry.body,
['this-class', 'that-class']) }}
{% macro richText(t, classes=[]) %}
{% if t|default('') %}
{{t|smartypants}}
{% endif %}
{% endmacro %}
Moral: deal with possibly missing parameters.
{% macro rNav(pages, depth=0) %}
{% import _self as self %}
<ul>
{% for page in pages|default([]) %}
<li>
{{ page.link }}
{% if page.hasDescendents() %}
{{ self.rNav(page.children, depth+1) }}
{% endif %}
</li>
{% endfor %}
</ul>
{% endmacro %}
{{ m_utils.rNav(craft.entries.section('sitePages')
.level(1)) }}
In twig, the context is all the values that are currently defined.
{% set foo = 'bar' %}
— adds foo
to the context.
_context
is a global variable which represents the current context.
{{ dump(_context|keys) }}
array(9) { [0]=> string(5) "craft"
[1]=> string(3) "blx" [2]=> string(3) "now"
[3]=> string(8) "loginUrl"
[4]=> string(9) "logoutUrl"
[5]=> string(8) "siteName" [6]=> string(7) "siteUrl"
[7]=> string(11) "currentUser" [8]=> string(4) "user"
}
_context
is not available in macros, but you can pass it in:
{% set meta = {title: 'myTitle',
description: 'some description'} %}
{{ m_meta.output(_context) }}
{% macro output(c) %}
{% import _self as self %}
{% if c.meta is defined %} {# in context? #}
{% set metaData = c.meta %}
{% elseif c.entry is defined %} {# have entry? #}
{% set metaData = {
title: c.entry.mTitle|default(c.entry.title),
description: c.entry.mDescription
} %}
{% else %} {# we got nothing #}
{% set metaData = {title: '', description: ''} %}
{% endif %}
{{self._output(metaData)}}
{% endmacro %}
{% macro _output(m) %}
{{m.title ? m.title~' | '}}{{siteName}}
{% if m.description %}
<meta name="description"
content="{{m.description}}"/>
{% endif %}
{% endmacro %}
{% for matrixBlock in entry.matrixField %}
{% include '_blocks/text' %}
{% endfor %}
The included template has access to the current context. matrixBlock
is defined.
We can be more explicit about this:
{% include '_blocks/text'
with {currentBlock: matrixBlock} only %}
currentBlock
will be defined... and nothing else
{% for matrixBlock in entry.matrixField %}
{% include '_blocks/'~matrixBlock.type %}
{% endfor %}
Will include the appropriate template for the block type (assuming you have defined it)
{% for matrixBlock in entry.matrixField %}
{% include ['_blocks/'~matrixBlock.type,
'_blocks/_default'] %}
{% endfor %}
Will include the template for the block type if it exists, otherwise _blocks/_default.html
{# this is 'child.html' #}
{% extends '_layouts/base.html' %}
{% set title = entry.title %}
{% block body %}
{{entry.body}}
{% endblock body %}
Since it extends another template, child.html
is not allowed to produce any output. It can add things to the context (e.g. title
), and define block contents.
{# this is '_layouts/base.html' #}
{{title}}
<body>
{% block body %}hello, world{% endblock body %}
{% block foot %}Copyright 2015{% endblock foot %}
</body>
title
will be defined here since it was set in child.html
The body
block will be replaced by the body
block from child.html
The foot
block will be left untouched.
Another layout template
{% include '_macros/_meta' as m_meta %}
<html>
<head>
{{ m_meta.output(_context) }}
</head>
<body>
{% block body %}{% endblock body %}
</body>
</html>
We have abstracted all of the output of the meta data into m_meta.output
, which since it has the context, is omniscient.
...
{% embed '_layouts/left_right.html' %}
{% block left %}...left content...{% endblock %}
{% block right %}...right content...{% endblock %}
{% endembed %}
...
embed
include
, the contents of the embed
will be replaced by _layouts/left_right.html
embed
, the contents of blocks in left_right.html
will be replaced by block content given here.embed
provides the same control of the context that include
does.
{% embed '_layouts/left_right.html'
with {foo: 'bar'} %}
...
{% endembed %}
{% embed '_layouts/left_right.html'
with {foo: 'bar'} only %}
...
{% endembed %}
with
and only
)
foo['bar']
foo
is an associative array with key bar
foo.bar
or attribute(foo, 'bar')
foo
is an associative array with key bar
foo
is object with property bar
foo
is object with method bar
foo
is object with method getBar
foo
is object with method isBar
foo|default('defaultValue')
These are all legal (and will return the default value):
neverHeardOfIt|default('')
neverHeardOfIt.someField|default('')
someArray[invalidIndex]|default('')
null|default('')
''|default('')
[]|default('')
false|default('')
0|default('')
will not return ''
merge
filter merges two arrays (regular or associative)
Use it to add something to an existing array
{% set a = ['this', 'that'] %}
{% set a = a|merge(['theOther']) %}
['this', 'that', 'theOther']
group
: Groups the items of an array together based on common properties.
{% set books = [
{ author: 'Brown', t: 'Goodnight Moon' },
{ author: 'Burnett', t: 'The Secret Garden' },
{ author: 'Sendak', t: 'The Nutshell Library' },
{ author: 'Seuss', t: 'The Cat in the Hat' }
] %}
{% set alphaBooks = books|group('author|first') %}
{ B:[{ author: 'Brown', t: 'Goodnight Moon' },
{ author: 'Burnett', t: 'The Secret Garden' } ],
S:[{ author: 'Sendak', t: 'The Nutshell Library' },
{ author: 'Seuss', t: 'The Cat in the Hat' } ]
}
find me at: