On Github definitivedrupal / NYCCampTwigTraining
Even in your theme.
<?php $variables['list'] = theme('item_list', array( 'items' => $items, ));
<?php $variables['list'] = array( '#theme' => 'item_list', '#items' => $items, );
Instead of the list variable being a flat string of HTML, it's a render array that gets rendered in a Twig template using {{ list }}.
Use #attached on a render array instead. (more on this later)
We have them! They use some things from SMACSS and BEM.
Twig is a modern template engine for PHP
…besides the fact that PHP Template is old and "broken"
There are 3 basic block delimiters in Twig:
And one special string interpolation delimiter.
Use filters to modify variables.
Docblock
<?php /** * @file * File description */ ?>
{# /** * @file * File description */ #}
File Names
node--article.tpl.php
node--article.html.twig
Function Names
theme_node_links()
node-links.html.twig
…that's right, all theme functions will now be template files!
Printing a variable
<div class="content"><?php print $content; ?></div>
<div class="content">{{ content }}</div>
Printing a hash key item
<?php print $item['#item']['alt']; ?>
{{ item['#item'].alt }}
Assigning a variable
<?php $custom_var = $content->comments; ?>
{% set custom_var = content.comments %}
Assigning an array
<?php $args = array('!author' => $author, '!date' => $created); ?>
{% set args = {'!author': author, '!date': created} %}
<?php if ($content->comments): endif; ?>
{% if content.comments %} {% endif %}
<?php if (!empty($content->comments)): endif; ?>
{% if content.comments is not empty %} {% endif %}
<?php if (isset($content->comments)): endif; ?>
{% if content.comments is defined %} {% endif %}
<?php if ($count > 0): endif; ?>
{% if count > 0 %} {% endif %}
<?php foreach ($users as $user) {} ?>
{% for user in users %} {% endfor %}
Check Plain (escaping)
<?php print check_plain($title); ?>
{{ title|e }}
Translate
<?php print t('Home'); ?>
{{ 'Home'|t }}
Translate w/ substitutions
<?php print t('Welcome, @username', array('@username' => $user->name)); ?>
{{ 'Welcome, @username'|t({'@username': user.name}) }} {% set username = user.name %} {% trans %} Welcome, {{ username }} {% endtrans %}
Implode list
<?php echo implode(', ', $usernames); ?>
{{ usernames|join(', ') }}
Escape
<?php echo check_plain($title); ?>
{{ title|e }} {{ title }}
You can use a dot (.) to access attributes of a variable (methods or properties of a PHP object, or items of a PHP array), or the so-called "subscript" syntax ([])
{{ foo.bar }} {{ foo['#bar'] }}
When the attribute contains special characters (like - that would be interpreted as the minus operator), use the attribute function instead to access the variable attribute:
{# equivalent to the non-working foo.data-foo #} {{ attribute(foo, 'data-foo') }}
There are still a lot of core theme functions that need your help!
<?php /* @file includes/theme.inc */ function theme_item_list($variables) { $items = $variables['items']; $title = $variables['title']; $type = $variables['type']; $attributes = $variables['attributes']; // Only output the list container and title, if there are any list items. // Check to see whether the block title exists before adding a header. // Empty headers are not semantic and present accessibility challenges. $output = '<div class="item-list">'; if (isset($title) && $title !== '') { $output .= '<h3>' . $title . '</h3>'; } if (!empty($items)) { $output .= "<$type" . drupal_attributes($attributes) . '>'; $num_items = count($items); $i = 0; foreach ($items as $item) { $attributes = array(); $children = array(); $data = ''; $i++; if (is_array($item)) { foreach ($item as $key => $value) { if ($key == 'data') { $data = $value; } elseif ($key == 'children') { $children = $value; } else { $attributes[$key] = $value; } } } else { $data = $item; } if (count($children) > 0) { // Render nested list. $data .= theme_item_list(array('items' => $children, 'title' => NULL, 'type' => $type, 'attributes' => $attributes)); } if ($i == 1) { $attributes['class'][] = 'first'; } if ($i == $num_items) { $attributes['class'][] = 'last'; } $output .= '<li' . drupal_attributes($attributes) . '>' . $data . "</li>\n"; } $output .= "</$type>"; } $output .= '</div>'; return $output; }?>
{# /core/modules/system/templates/item-list.html.twig #} {%- if items or empty -%} <div class="item-list"> {%- if title -%} <h3>{{ title }}</h3> {%- endif -%} {%- if items -%} <{{ list_type }}{{ attributes }}> {%- for item in items -%} <li{{ item.attributes }}>{{ item.value }}</li> {%- endfor -%} </{{ list_type }}> {%- else -%} {{- empty -}} {%- endif -%} </div> {%- endif %}
<?php /* includes/theme.inc */ function theme_links($variables) { $links = $variables['links']; $attributes = $variables['attributes']; $heading = $variables['heading']; global $language_url; $output = ''; if (count($links) > 0) { $output = ''; // Treat the heading first if it is present to prepend it to the // list of links. if (!empty($heading)) { if (is_string($heading)) { // Prepare the array that will be used when the passed heading // is a string. $heading = array( 'text' => $heading, // Set the default level of the heading. 'level' => 'h2', ); } $output .= '<' . $heading['level']; if (!empty($heading['class'])) { $output .= drupal_attributes(array('class' => $heading['class'])); } $output .= '>' . check_plain($heading['text']) . '<!--' . $heading['level'] . '-->'; } $output .= '<ul' . drupal_attributes($attributes) . '>'; $num_links = count($links); $i = 1; foreach ($links as $key => $link) { $class = array($key); // Add first, last and active classes to the list of links to help out themers. if ($i == 1) { $class[] = 'first'; } if ($i == $num_links) { $class[] = 'last'; } if (isset($link['href']) && ($link['href'] == $_GET['q'] || ($link['href'] == '<front>' && drupal_is_front_page())) && (empty($link['language']) || $link['language']->language == $language_url->language)) { $class[] = 'active'; } $output .= '<li' . drupal_attributes(array('class' => $class)) . '>'; if (isset($link['href'])) { // Pass in $link as $options, they share the same keys. $output .= l($link['title'], $link['href'], $link); } elseif (!empty($link['title'])) { // Some links are actually not links, but we wrap these in <span> for adding title and class attributes. if (empty($link['html'])) { $link['title'] = check_plain($link['title']); } $span_attributes = ''; if (isset($link['attributes'])) { $span_attributes = drupal_attributes($link['attributes']); } $output .= '<span' . $span_attributes . '>' . $link['title'] . '</span>'; } $i++; $output .= "</li>\n"; } $output .= '</ul>'; } return $output; }?>
{# /core/modules/system/templates/links.html.twig #} {% if links -%} {%- if heading -%} {%- if heading.level -%} <{{ heading.level }}{{ heading.attributes }}>{{ heading.text }}</{{ heading.level }}> {%- else -%} <h2{{ heading.attributes }}>{{ heading.text }}</h2> {%- endif -%} {%- endif -%} <ul{{ attributes }}> {%- for item in links -%} <li{{ item.attributes }}> {%- if item.link -%} {{ item.link }} {%- elseif item.text_attributes -%} <span{{ item.text_attributes }}>{{ item.text }}</span> {%- else -%} {{ item.text }} {%- endif -%} </li> {%- endfor -%} </ul> {%- endif %}
<?php /* includes/theme.inc */ function theme_breadcrumb($variables) { $breadcrumb = $variables['breadcrumb']; if (!empty($breadcrumb)) { // Provide a navigational heading to give context for breadcrumb links to // screen-reader users. Make the heading invisible with .element-invisible. $output = '<h2 class="element-invisible">' . t('You are here') . '</h2>'; $output .= '<div class="breadcrumb">' . implode(' >> ', $breadcrumb) . '</div>'; return $output; } }?>
{# /core/modules/system/templates/breadcrumb.html.twig #} {% if breadcrumb %} <nav class="breadcrumb" role="navigation"> <h2 class="visually-hidden">{{ 'You are here'|t }}</h2> <ol> {% for item in breadcrumb %} <li>{{ item }}</li> {% endfor %} </ol> </nav> {% endif %}
Developers can change the environment variables in settings.php to allow for different configuration per environment. Drupal core uses the Settings API to access this information when building the Twig environment.
$settings['twig_debug'] = TRUE;
Twig Documentation is easy to get to, just like it is for PHP.net
Reusable markup templates with variable insertion
Think: PHP Template functions for markup
{# menu.twig #} {% macro menu_links(links) %} {% if links %} <ul> {% for link in links %} <li> <a href="{{ link.href }}">{{ link.name }}</a> {% if link.links %} {{ _self.menu_links(link.links) }} {% endif %} </li> {% endfor %} </ul> {% endif %} {% endmacro %}
{# somewhere in a twig template in our theme #} {% import "menu.twig" as menu %} <nav> {{ menu.menu_links(links) }} </nav>