If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
You're a pro now, so let's have a little fun and see some sweet tricks!
We've learned a lot about template inheritance and blocks. Now, let's make
things a bit more interesting. We created a title
block in our layout
so that individual pages could control the page title. If a page has a title
block, it replaces the page title entirely. If it has no title
block,
then the default title is used.
Let me change the title
block to be a little more interesting:
<title>
{% block title %}
Penguins Pants Plus! Your source for fancy penguin suits
{% endblock %} | Penguins Pants Plus!
</title>
My goal is to suffix the page title with "Penguins Pants Plus!" so that all
of our pages are consistent. The only funny thing here, besides penguin pants,
is that if the title
block isn't overridden, the suffix is a little redundant.
Is it possible to only add the extra text if the title
block is overridden?
The secret is the block function, which you can use to return the content
of a block at any time. Replace the traditional {% block title %}
and
instead use the block()
function in a say something
tag:
{{ block('title') }} | Penguins Pants Plus!
When we refresh, this works like before: the block
function prints out
the content from the title
block. The only thing we're missing is the
default page title if the title
block isn't set.
Now that we know about this block
function, we can do that easily with
an if
statement:
{% if block('title') %}
{{ block('title') }} | Penguins Pants Plus!
{% else %}
Penguins Pants Plus! Your source for fancy penguin suits
{% endif %}
Tip
The block()
function throws an exception since Twig 2.0 if there's
no block with specified name. At first, be sure that the block is defined:
{% if block('title') is defined %}
{{ block('title') }} | Penguins Pants Plus!
{% else %}
Penguins Pants Plus! Your source for fancy penguin suits
{% endif %}
Success! When we override the title
block on the homepage, we get the
suffix added. But on the contact page, we just get the default page title.
I try to use blocks in their traditional fashion as often as possible. But
when things get more complicated, use the block
function to do some really
custom things.
Go Deeper!
To get really advanced, you can import blocks from other templates and use them in this way. See the use tag for more details.
And while we're on this topic, we can make the title
block even shorter
in homepage.twig
:
{% block title 'Start looking fly!' %}
You're free to choose whatever format you want, but if your block is just a simple string, you'll often see this version used.
One apparent drawback to this is that you can't mix static text and variables like you could before by just writing some text and then using the "say something" syntax.
For example, suppose we wanted to include the pageData.title
variable
in the page title. How can we combine it with the static text? The answer
is with the ~
character, which concatenates strings in Twig.
{% block title 'Start looking fly! '~pageData.title %}
You won't see this too often, but it'll come in handy when you need it.
Normally, the whitespace you put in a Twig file is left completely alone. We can
see this when we view the source. In fact, we have some extra space around
the title
tag because of the new trick we're using in Twig. Let's see
if we can get rid of it!
On any twig starting or ending tag, you can add a minus sign (-
):
<title>
{%- if block('title') %}
{{ block('title') }} | Penguins Pants Plus!
{% else %}
Penguins Pants Plus! Your source for fancy penguin suits
{% endif %}
</title>
This tells Twig to trim all the whitespace to the left of that tag until it hits a non-whitespace character. When we view the source, we can see a slightly smaller amount of whitespace. If we add enough of these, we'll see all the extra space disappear:
<title>
{%- if block('title') -%}
{{- block('title') -}} | Penguins Pants Plus!
{%- else -%}
Penguins Pants Plus! Your source for fancy penguin suits
{%- endif -%}
</title>
Another way to control whitespace is with the spaceless tag. The point of this tag is a little different: it removes all whitespace between HTML tags, without affecting space inside an HTML tag or inside static text. If we surround the meta tags with this and refresh, we'll see those meta tags all print right next to each other on one line:
{% spaceless %}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="">
<meta name="author" content="">
{% endspaceless %}
Let's see one more common trick that may look strange when you first see
it. Look back in the banner.twig
template where we used the
single-line if syntax. Actually, there's an
easier way to do this by using the default filter:
<div class="well" style="background-color: {{ backgroundColor|default('lightblue') }};">
Normally, if you reference an undefined variable in Twig, it blows up! But
when you use the default
filter, it avoids that error and instead, returns
the default value lightblue
. You may see this trick quite often when
someone is using a variable that may or may not be defined.
Tip
Depending on your settings, Twig may just fail silently if you reference an undefined variable.
Ok, one last thing - HTML escaping! Whenever you render content that may have been filled in by the user, you need to escape it. This prevents people from writing HTML tags that you don't want or, worse, JavaScript code that could be used for cross-site scripting attacks. That's scarier than a hungry pack of leopard seals!
Let's try this out by adding some HTML markup to our page summary:
// index.php
// ...
echo $twig->render('homepage.twig', array(
'pageData' => array(
'summary' => "You're <strong>hip</strong>, you're cool ...",
),
// ...
));
When we refresh, Twig is automatically escaping these characters and printing them out safely. Actually, whether or not Twig automatically does this depends on how it's setup. In your case, try this out and see if Twig is escaping or not escaping automatically. You can try this easily by printing out a static string and seeing what happens.
{{ '<strong>hallo</strong>'|upper }}
In some cases, you may need to actually print out some content unescaped. To do this, just use the handy raw filter:
<p>
{{ pageData.summary|raw }}
</p>
Tip
If automatic escaping is off, then you need to be quite careful and use the escape filter on any strings you print out to make sure they are escaped.
Well hello Twig expert! Our time talking about Twig is coming to an end, but the good news is that you have all the tools you need to be successful and your penguins are looking dapper. Remember that all the tags, functions, filters and tests that are available in Twig can be found on the bottom of its documentation page.
Also remember that in your project, you may have even more tags, functions, filters or tests that are specific to you. Your challenge from here is to find out what those are and what secrets each holds.
Good luck, and seeya next time!
Hey RoGluee ,
Thanks for noticing us! Actually, it's interesting! I wonder what Twig version do you use? Is it the new 2.x? Because in older version 1.x of Twig I don't have that error for non-existent blocks.
Cheers!
you get a deprecation warning in recent 1.x versions (starting at the version where `block('title') is defined` became a valid syntax)
Ah, nevermind about https://knpuniversity.com/s... . I already double checked it: the problem only affects Twig >=2.0. We'll add a note. Thank you for this report!
Cheers!
Hi I really like your videos about twig, I learned a lot. You have done a great work and make learning twig easy
But seriously Penguin, I m looking videos at work and my boss seems to think I'm surfing on the internet and not working :-( There are penguins all over the screen
Hey eCosinus ,
Haha, sorry.. probably too much penguins in one screencast :p
And thanks for the kind words!
Cheers!
// composer.json
{
"require": []
}
If there is no block title you get error - (Block "title" on template "sth.html.twig" does not exist).
So {% if block('title') %} should be changed to {% if block('title') is defined %}