Be Hoppy
Daniel Gilbert, a Harvard psychology professor, did a study in happiness with a photography class. Every student in the class was directed to take two pictures. The students were then divided into two groups. Members of Group A were charged with choosing between the two photos. They were allowed to keep their favorite of the two, and the other photo was permanently relinquished to the teacher. Group B was given the same task, except they were allowed several days to reconsider and exchange the print they’d originally chosen for the one they’d relinquished. The students were surveyed at the conclusion of the experiment and the results showed that Group A was far and away happier with the project than Group B.
The moral of the story is that having too many options can make us second guess ourselves and possibly lead to dissatisfaction with our outcome. In Drupal-related scenarios, Twig can be likened to Group A and PHP to Group B. For example, in Drupal 7 if you wanted to override markup you had to first determine whether the markup was coming from a PHP template file or being generated by a function; you had a lot of options and things could get disorienting.
While markup can still be generated by function and other logic, Drupal 8 does a much better job of using the Twig templating system instead of strings created by PHP functions. Again, Twig is super extendable, so if you want to do it the old, complicated, and potentially damaging way you can, but you don’t have to. In short, Twig is proof that Drupal contributors and SensioLabs love us and want us to be happy.
This is just one of the many ways in which templating has been simplified in Drupal 8 compared to previous releases. In this post, we’ll cover the history of Twig and why it’s replacing PHPTemplate, a few of its many wonderful qualities, and how to use it in creating templates for your custom theme in a little more detail.
Twig: A Brief History
Twig was originally written by Armin Ronacher, creator of Jinja, in 2008. He’d written it for the blogging platform Chypr but, primarily being a Python developer, didn’t do much with it following its original creation.
Fabien Potencier, author of the Symphony framework and CEO of SensioLabs, began his search for an alternative to straight PHP in 2009. While PHP is inarguably effective, hence why Twig still compiles to PHP, it is far too verbose for general templating. He wanted an engine with features similar to Django. During his search he considered Smarty, PHPTAL, DWOO, eZ Templates, and Calypso before finding Twig. Fabien required the following features in a templating language: succinctness, security, and usability.
With Armin’s permission, Fabien began hacking Twig. The end result was significantly different from the previous version of Twig with the exception of the original lexer and parser remaining largely unchanged. Fabien’s highly extensible Twig featured native template inheritance, automatic escaping (autoescaping), and a secure sandbox mode.
When measured against the other templating engines Fabien had considered, Twig turned out to be the fastest while still requiring the least amount of memory. In addition to being fast and efficient, Twig also featured excellent documentation.
Simple, Secure & DRY
Simple
While Drupal 8 is still a powerfully complex CMS, Twig makes the language more intuitive. PHP is verbose, so even simple operations can require a lot of code, which leaves more room for error. Here is an example provided on Fabien’s blog of PHP output escaping, which is mandatory for safety purposes.
And here is the same output escaping done using Twig.
Pretty good, right?
Secure
When writing PHP, variables have to be escaped manually. This not only takes a significant amount of time but forgotten or missed variables can result in security issues. In addition to manual escaping, PHP doesn’t have a sandbox mode for restricting what users can and cannot do. Both of these issues have been resolved in Twig.
Autoescaping is enabled by default in Twig and the resulting compiled PHP, so all the user has to be responsible for escaping manually are JavaScript variables. The built-in sandbox environment allows you to restrict what can be done with a template by white-listing methods, functions, and tags.
DRY
Personally, template inheritance is my favorite part of Twig. Not only is it an incredibly organized approach to development, it’s also remarkably easy. Here’s how it works:
First, you create a base template:
Then, extend that template with the {% extends %} function.
Syntax
Twig’s simplistic syntax makes it fairly easy to learn. Twig has three sets of delimiters, all beginning with curly brackets.
Booplesnoots
First and foremost (quite literally), a Booplesnoot is the very beginning of a bunny. It is followed closely by the Floppity Loppities and remains the furthest point from the venerated Poof Loof. Please don’t be alarmed if you find yourself unfamiliar with this terminology, it is not necessary for learning more about Twig templating. However, it is featured on the example site I’ll be referring to, so you may find it helpful to acquaint yourself with the image below.
Twig, and Drupal for that matter, are a great deal more like the noble bun (short for bunny) than one might think at first glance. Like a bun, Twig and Drupal can appear sleek and simple on the surface, but beneath that well-composed layer lies a body of conglomerate inner workings and relationships. Twig is more easily written than PHP, but it still compiles to PHP so it is powerful. Like Drupal 8, though it is more streamlined than its predecessors, is still exceedingly flexible and therefore complex.
Printing Things
As described in the syntax section, Twig has two main jobs, doing things and printing things. In this section, we’ll focus on the printing things part. If you remember the first post in this series, “Drupal 8 Theme Basics”, you’re likely familiar with regions. It’s been a while though, so let’s recap. The only region required by Drupal is the content region; aside from that, you can have as many regions as you want. The theme that I’m most familiar with is Bartik, so I used the same regions defined there in my theme. More information on regions can be found in your page.html.twig template.
- page.header – Items for the header region.
- page.highlighted – Items for the highlighted region.
- page.primary_menu – Items for the primary menu region.
- page.secondary_menu – Items for the secondary menu region.
- page.highlighted – Items for the highlighted content region.
- page.help – Dynamic help text, mostly for admin pages.
- page.content – The main content of the current page.
- page.sidebar_first – Items for the first sidebar.
- page.sidebar_second – Items for the second sidebar.
- page.footer – Items for the footer region.
- page.breadcrumb – Items for the breadcrumb region.
These regions refer to the block layout inherent in Drupal core. If you’re new to Drupal, you can see the block layout for Drupal regions in your admin area by going to Structure > Block layout and clicking “Demonstrate block regions (Theme Name)”.
How cute is that? Anyway…
When creating a Twig template we essentially determine where we would like content to display on our site, write basic HTML, and then reference the appropriate region. For example, this is my theme’s page.html.twig:
Here, you can see that my regions are clearly defined and, by comparing the screenshot of my template with the one of the Drupal block layout above it, that these regions correspond to my blocks layout. Also, because I’m using Stable as a base theme, I didn’t even have to write the template HTML myself. All I had to do was go into the Stable theme’s template folder, copy the template I wanted to edit, save it in my theme, and make my changes. You’ll notice that there are a couple of ‘if’ statements in the mix, and we’ll discuss those shortly.
Now, to display my entire page’s content I’ve simply included the Twig statement {{ page.content }}–nothing wrong with that. However, if I want to drill down deeper on individual pages, I have that option too.
First, because I enjoy doodling, bunnies, and fun, I would like you to meet Fluffy.
Artistic interpretations aside, believe me when I say that Fluffy is a bunny. As such, Fluffy has a healthy fear of owls because they’re the sworn enemies of lagomorphs everywhere for obvious reasons.
To demonstrate a slightly more intricate use of Twig than {{ page.content }} as well as tell you more about Fluffy and aid him in his mission to educate his fellow buns on the dangers of owls, I’ve created an example site called Booplesnoot.dev.
In addition to the homepage, which provides a little background on bunny anatomy and several arguments concerning why owls should be avoided, Booplesnoot.dev features two pages: Simple Bunny and Complex Bunny.
Simple Bunny is just that–simple. It uses {{ page.content }} to display all of the content in the Body field WYSIWYG editor on the page.
Here’s the page:
Here’s the Drupal back-end:
Here’s my node template for displaying this content. Please note that because this template file represents a node rather than a page there are different variables available to us; I used {{ content }} to display the content in the Drupal Body field rather than {{ page.content }}.
Complex Bunny front-end on the other hand…looks exactly the same as Simple Bunny.
Just in case you didn’t believe me.
The “complex” part (and I’m using the term loosely) of Complex Bunny is in the Drupal back-end and the node template.
Complex Bunny is generated by a different page content type than Simple Bunny, which consists of multiple fields, not just the Body field**. In order to display each field the Twig print-something statements in my template change. Complex Bunny is node 4 and here is the corresponding template in my theme.
Rather than just {{ content }} we’re drilling deeper into the content by referencing specific fields in the content using their machine names. By referencing fields specifically I’m able to dictate what content is displayed, where it is displayed, as well as what content does not display. For example, you’ll notice that there are two fields in the Complex Bunny page that aren’t displaying at all.
Doing Things
Much like the Wizard of Oz telling Dorothy and company to “Ignore that man behind the curtain!”, in the last section I told you to disregard some if statements that were hanging about our printables–let’s address those now.
As we saw in the Syntax section of this powst, Twig has three basic delimiters:
- {{ print something }}
- {% do something %}
- {# comment #}
We use the {% do something %} delimiters when we want to add a piece of logic, like a function. Again, this is where the whole cute-like-a-bunny-but-complex-like-an-evil-genius bit comes in for Twig–you can do a LOT of things, both simple and complex, with some very basic-looking Twig. Since I’m learning as I go, I’m going to start with an über simple piece of logic. Ladies and gentleman, I give you an ‘if’ statement:
Above is another screenshot of my Complex Bunny template, this time with the aforementioned statement. It’s not too fancy, but I ran into a couple snags when writing it, so allow me to explain.
This first line assesses whether or not the raw value of the node* field with the machine name “bun_thing_4” is empty by using the length filter to compare it’s value to zero.
If the field value is greater than zero (meaning it has a value), then {{ content.field_bun_thing_4 }} is displayed on the page with some pretty styling.
Here’s what that gets us.
Next we have the else portion of the if statement that applies if the raw node field value is blank, or not greater than zero.
The result is not nearly as cute.
Twig is capable of a great deal more than simple functions, including the use of filters, macros, and control structures. You can learn more about them and find excellent examples in the Twig documentation, specifically the Twig for Template Designers section provided by SensioLabs.
Fin
Thank you for taking the time to read my post! Summer is a busy time at Bōwst, but I hope to have another out in the next few weeks that addresses integrating JavaScript into Drupal 8 themes. Until then, if you want to talk Drupal with some pros, consider attending a Seacoast DUG meeting (I’m so sorry for what’s about to happen…) where Bōwst is the host with the most! In this scenario, by “most” I mean pizza, beer, and ACQUIA CERTIFIED DRUPAL DEVELOPERS!! I don’t mean to bōwst (Again, my apologies) but JP got his certification last year and Drew(pal) Trafton earned his just a few weeks ago, so we’re incredibly proud of them both. DUG meetings are the first Thursday of every month and be sure to follow us on Facebook and Twitter for updates on hour-long Lunch and Learns we host throughout the month as well.
Until next time–stay fluffy.
*—In my first couple attempts I’d tried to use “content.field_bun_thing_4” in my statement rather than “node.field_bun_thing_4”. This didn’t work because “content” refers to the field after it’s been rendered and what I needed to do for my if statement to make sense was look at the raw field, which is why Bōwst’s very own Drew(pal) (Once again, a recently Acquia certified Drupal Developer, everybody–WOOOOO!!!!) recommended I use “node” instead.
**–I used the Field Formatter Class module to add classes to my fields to make them easier to target with CSS.
For the previous post in this series, click here.
For the next post in this series, click here!