Drupal 8 The Backend of Frontend
Lauri Eskola (lauriii)
Front end track | Twitter: @laurii1
Lauri Eskola
- Drupal Theme System maintainer
- Working for Druid
- I love kittens (added 68 of them to Drupal 8 Core and counting...)
- ... and I like to break Bartik.
Front end track | Twitter: @laurii1
Drupal Theme System
Provides flexible way to output safe HTML
Drupal 8 ships with simplified theme layer
Drupal 8 - Be lazy!
Laziness is the first step towards efficiency.
― Patrick Bennett
Drupal 7 Template process layer
<?php
function template_process_html(&$variables) {
// Render page_top and page_bottom into top level variables.
$variables['page_top'] = drupal_render($variables['page']['page_top']);
$variables['page_bottom'] = drupal_render($variables['page']['page_bottom']);
// Place the rendered HTML for the page body into a top level variable.
$variables['page'] = $variables['page']['#children'];
$variables['page_bottom'] .= drupal_get_js('footer');
$variables['head'] = drupal_get_html_head();
$variables['css'] = drupal_add_css();
$variables['styles'] = drupal_get_css();
$variables['scripts'] = drupal_get_js();
}
Wastes resources if something doesn't get printed
Template process layer has been removed.
Everything is render array!
Drupal 8
<?php
$variables['list'] = theme('item_list', array(
'items' => $items,
));
Drupal 7
<?php
$variables['list'] = [
'#theme' => 'item_list',
'#items' => $items,
];
<php
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;
}
{%- if items or empty -%}
<div class="item-list">
{%- if title is not empty -%}
<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 %}
Theme functions are being converted to Twig templates...
Drupal 7 vs Drupal 8: 154 / 8
Drupal 8 Theme System pipeline
Code examples:
Drupal 8 Theme System pipeline
hook_theme
<?php
/**
* Implements hook_theme().
*/
function sandwich_theme() {
return [
'sandwich' => [
'variables' => [
'attributes' => [],
'name' => '',
'bread' => '',
'cheese' => '',
'veggies' => [],
'protein' => '',
'condiments' => [],
],
],
];
}
Build your render array
<?php
public function build() {
return [
'#theme' => 'sandwich',
'#name' => $this->t('Chickado'),
'#attributes' => [
'id' => 'best-sandwich',
'class' => ['menu--left', 'clearfix'],
],
'#bread' => $this->t('Sourdough'),
'#cheese' => $this->t('Gruyère'),
'#veggies' => [
$this->t('Avocado'),
$this->t('Red onion'),
],
'#protein' => $this->t('Chicken'),
'#condiments' => [
$this->t('Mayo'),
$this->t('Dijon'),
],
];
}
Drupal 8 Theme System pipeline
Theme suggestions
node.html.twig
node--article.html.twig
hook_theme_suggestion and hook_theme_suggestion_alter
<?php
/**
* Implements hook_theme_suggestions_sandwich().
*/
function sandwich_theme_suggestions_sandwich($variables) {
return 'sandwich__' . strtolower($variables['name']);
}
/**
* Implements hook_theme_suggestions_sandwich_alter().
*/
function sandwich_theme_suggestions_sandwich_alter(&$suggestions, $variables) {
}
Drupal 8 Theme System pipeline
template_preprocess and
hook_preprocess
<?php
/**
* Implements template_preprocess_sandwich().
*/
function template_preprocess_sandwich(&$variables) {
$variables['name'] = 'Kitten';
}
/**
* Implements hook_preprocess_sandwich().
*/
function sandwich_preprocess_sandwich(&$variables) {
$variables['name'] = 'Llama';
}
/**
* Implements hook_preprocess_sandwich__chickado().
*/
function sandwich_preprocess_sandwich__chickado(&$variables) {
$variables['name'] = 'Flamingo';
}
template_preprocess and
hook_preprocess
<?php
/**
* Implements template_preprocess_sandwich().
*/
function template_preprocess_sandwich(&$variables) {
$variables['name'] = t('Kitten');
if (!empty($variables['name']['#machine_name'])) {
$variables['name'] = $variables['name']['#machine_name'];
}
}
Drupal 8 Theme System pipeline
sandwich.html.twig
<section{{ attributes }}>
<h2>{{ name }}</h2>
{% if bread %}
<p><strong>Bread:</strong> {{ bread }}</p>
{% endif %}
{% if protein %}
<p><strong>Protein:</strong> {{ protein }}</p>
{% endif %}
{% if cheese %}
<p><strong>Cheese:</strong> {{ cheese }}</p>
{% endif %}
{% if veggies %}
<strong>Veggies:</strong>
<ul>
{% for veg in veggies %}
<li>{{ veg }}</li>
{% endfor %}
</ul>
{% endif %}
{% if condiments %}
<strong>Accoutrement:</strong>
<ul>
{% for condiment in condiments %}
<li>{{ condiment }}</li>
{% endfor %}
</ul>
{% endif %}
</section>
Download the code:
Twig - what you really need to know.
Twig magic
{{ sandwich.cheese }}
// Array key.
$sandwich['cheese'];
// Object property.
$sandwich->cheese;
// Also works for magic get (provided you implement magic isset).
$sandwich->__isset('cheese'); && $sandwich->__get('cheese');
// Object method.
$sandwich->cheese();
// Object get method convention.
$sandwich->getCheese();
// Object is method convention.
$sandwich->isCheese();
// Method doesn't exist/dynamic method.
$sandwich->__call('cheese');
Twig filters
Meant to manipulate a variable. Takes the first parameter from the variable before "|".
{% set name = 'Lauri' %}
{# Print varibale using lenght filter. #}
{{ name|length }}
Example
Returns
5
Twig functions
Functions with more logic and multiple parameters meant to create simple front-end logic
{{ dump() }}
Example
Print what you want, when you want
{# We give you what you ask for. #}
{{ content|without('comments', 'links') }}
Drupal 7
Drupal 8
<?php
// We hide the comments and links now so that we can render them later.
hide($content['comments']);
hide($content['links']);
print render($content);
Sometimes what Twig has built inside might not be enough...
How to create a Twig extension
1. Create a Twig extension class
<?php
/**
* A class providing my own Twig extension.
*/
class TrimString extends \Twig_Extension {
/**
* {@inheritdoc}
*/
public function getFilters() {
return [
new \Twig_SimpleFilter('trim_string', [$this, 'trimString']),
];
}
public function trimString($string, $length = 10) {
return substr($string, 0, $length);
}
}
2. Tell Twig about your class
services:
trim_string.twig.trimstring:
class: Drupal\trim_string\TwigExtension\TrimString
tags:
- { name: twig.extension }
trim_string.services.yml
3. Profit! Go to the beach!
{% set variable = 'my text' %}
{{ variable|trim_string(2) }}
my
Returns
Example:
https://github.com/lauriii/trim-string
Autoescape
Markup should live inside a Twig template. Not in PHP!
What is escaping?
<?php
use Drupal\Component\Utility\Html
print Html::escape('Some escaped <em>text</em>');
Prints markup
Some escaped <em>text</em>
Some escaped <em>text</em>
What is Autoescaping?
{{ markup }}
Prints markup
Some escaped <em>text</em>
Some escaped <em>text</em>
<?php
function sandwich_preprocess_sandwich(&$variables) {
$variables['markup'] = '<em>text</em>';
}
How to avoid autoescaping?
{{ markup }}
Prints markup
Some escaped <em>text</em>
Some escaped text
<?php
function sandwich_preprocess_sandwich(&$variables) {
$variables['markup']['#markup'] = '<em>text</em>';
}
How to avoid autoescaping?
{{ markup }}
Prints markup
Some escaped <em>text</em>
Some escaped text
<?php
use Drupal\Component\Utility\SafeMarkup;
function sandwich_preprocess_sandwich(&$variables) {
$variables['markup'] = SafeMarkup::format('<em>@txt</em>', ['@txt' => 'text']);
}
But it has also its caveats...
When autoescaped strings are safe?
When the escaped string is being printed as HTML.
Attributes are NOT HTML!
<?php
use Drupal\Component\Utility\SafeMarkup;
SafeMarkup::format('<em@txt></em>', ['@txt' => 'text']);
SafeMarkup::format('<a href="@url"></a>', ['@url' => 'http://kittens.com']);
SafeMarkup::format('<a href="@url"></a>', ['@url' => 'javascript:alter(0)']);
New placeholder for URLs
<?php
use Drupal\Component\Utility\SafeMarkup;
SafeMarkup::format('<a href=":url"></a>', [':url' => 'javascript:alter(0)']);
Autoescaping is only enabled for Twig templates
Which means using PHP Template or Theme functions is not safe by default
Finishing this API is the only thing still blocking Drupal 8 RC1.
You can support us by testing!
Sprint: Friday
Sprint with the Community on Friday.
We have tasks for every skillset.
Mentors are available for new contributors.
An optional Friday morning workshop for first-time sprinters will help you get set up.
Follow @drupalmentoring.
https://www.flickr.com/photos/amazeelabs/9965814443/in/faves-38914559@N03/
Questions?
Front end track | Twitter: @laurii1
DrupalCon 2015 - Drupal 8 The Backend of the Frontend
By lauriii
DrupalCon 2015 - Drupal 8 The Backend of the Frontend
- 1,644