Boris Böhne
Drupal Developer, Sindelfingen, Germany
07/02/2015
function mymodule_block_content() {
$output = '';
$users = mymodule_get_some_users();
$output = '<ul class="list" style="margin-top:10px">';
foreach ($users as $user) {
$output .= '<li class="item"><em>' . $user->name . '</em></li>';
}
$output .= '</ul>';
return $output
}
function mymodule_block_content() {
$users = mymodule_get_some_users();
$output = theme('mymodule_block', $users);
return $output
}
function mymodule_theme() {
return array(
'mymodule_block' => array(
'variables' => array('users' => NULL),
),
);
}
function theme_mymodule_block($variables) {
$users = $variables['users'];
$output = '<ul class="list" style="margin-top:10px">';
foreach ($users as $user) {
$output .= '<li class="item"><em>' . $user->name . '</em></li>';
}
$output .= '</ul>';
return $output
}
function mymodule_block_content() {
$users = mymodule_get_some_users();
$output = theme('mymodule_block', $users);
return $output
}
function mymodule_theme() {
return array(
'mymodule_block' => array(
'variables' => array('users' => NULL),
'template' => 'mymodule_block'
),
);
}
and in mymodule_block.tpl.php:
<ul class="list" style="margin-top:10px">
<?php foreach ($users as $user) : ?>
<li class="item"><em><?php print $user->name; ?></em></li>
<?php endforeach; ?>
</ul>
(aka Theme Engine)
Drupal 4.7 / 5 / 6 / 7: PHPTemplate
Drupal 8: TWIG
Variables
- config
- schema
- mytheme.schema
- css
- base.css
- layout.css
- images
- js
- special.js
- libraries
- flexslider
- templates
- node.html.twig
- page.html.twig
logo.svg
screenshot.png
mytheme.breakpoints.yml
mytheme.info.yml
mytheme.libraries.yml
mytheme.theme
/themes/custom/mytheme/
name: 'My theme'
type: theme
description: 'Just a sample theme for Drupal 8.'
package: Custom
core: 8.x
libraries:
- mytheme/global
stylesheets-remove:
- core/assets/vendor/normalize-css/normalize.css
regions:
header: Header
content: Content
sidebar_first: 'Sidebar first'
footer: Footer
/themes/custom/mytheme/mytheme.info.yml
# This one is specified in mytheme.info.yml and loaded on all pages
global:
version: VERSION
css:
theme:
css/base.css {}
css/layout.css: {}
# Those are needed in some special places and loaded via #attached['library']
special:
version: VERSION
css:
theme:
css/special.css: {}
js:
js/special.js: {}
flexslider:
version: '2.5.0'
css:
theme:
libraries/flexslider/flexslider.css: {}
js:
libraries/flexslider/jquery.flexslider.js: {}
dependencies:
- core/jquery
/themes/custom/mytheme/mytheme.libraries.yml
<?php
/**
* Implements hook_element_info_alter().
*/
function mytheme_element_info_alter(&$type) {
// We require Modernizr for button styling.
if (isset($type['button'])) {
$type['button']['#attached']['library'][] = 'core/modernizr';
}
}
/themes/custom/mytheme/mytheme.theme
Formerly known as template.php!
mytheme.mobile:
label: mobile
mediaQuery: ''
weight: 2
multipliers:
- 1x
mytheme.narrow:
label: narrow
mediaQuery: 'all and (min-width: 560px) and (max-width: 850px)'
weight: 1
multipliers:
- 1x
mytheme.wide:
label: wide
mediaQuery: 'all and (min-width: 851px)'
weight: 0
multipliers:
- 1x
/themes/custom/mytheme/mytheme.breakpoints.yml
Main difference: no more theme() functions!
(no more theme functions!)
function mymodule_theme($existing, $type, $theme, $path) {
return array(
'mymodule_something' => array(
'variables' => array('something' => NULL, 'otherthing' => NULL),
),
);
}
hook_theme(), in your module file
(*.html.twig, not *.tpl.php)
{% if something %}
<p>
{{ something }}
{{ otherthing }}
</p>
{% endif %}
mymodule-something.html.twig, in your module's template folder (/modules/custom/mymodule/templates)
(using render array, not theme())
...
$output = array(
'#theme' => 'mymodule_something',
'#something' => 'Something',
'#otherthing' => 'Otherthing',
);
...
Somewhere in your code (controller, plugin, ...)
(mainly provided by other modules)
/**
* Implements hook_preprocess_HOOK().
*/
function yourmodule_preprocess_mymodule_something(&$variables) {
...
}
In your module file or theme
Print / Output:
Comments:
Execute statements:
Simple variables (strings, numbers, booleans):
{{ foo }}
Complex variables (arrays / objects):
Objects & arrays:
{{ foo.bar }}
Alternative:
{{ attribute(foo, 'bar') }}
Only arrays:
{{ foo['bar'] }}
Example from views module:
{% if attributes -%}
<div{{ attributes }}>
{% endif %}
{% if title %}
<h3>{{ title }}</h3>
{% endif %}
<{{ list.type }}{{ list.attributes }}>
{% for row in rows %}
<li{{ row.attributes }}>{{ row.content }}</li>
{% endfor %}
</{{ list.type }}>
{% if attributes -%}
</div>
{% endif %}
{% set foo = 'bar' %}
{% set foo, bar = 'foo', 'bar' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
{% set foo = 'foo' ~ 'bar' %}
{% set foo %}
<div id="pagination">
...
</div>
{% endset %}
More examples:
http://twig.sensiolabs.org/doc/tags/set.html
{% if ordered_list %}
<ol>
{% else %}
<ul>
{% endif %}
IF ... THEN ... ELSE ... ENDIF
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
FOR ... ENDFOR
{% include '@mytheme/parts/footer.html.twig' %}
INCLUDE -> fetch another template file
{% block footer %}
© Copyright 2015 drubb
{% endblock %}
BLOCK -> section that can be repeated, or overwritten by other template files
{{ title|upper|trim('.') }}
{% filter upper %}
This text becomes uppercase
{% endfilter %}
More examples: abs, first, last, length, lower, reverse, round,...)
http://twig.sensiolabs.org/doc/filters/index.html
General Twig filters
Drupal specific filters
Translate:
<a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home" class="site-logo"></a>
Without:
{{ content|without('links') }}
Safe join:
{{ items|safe_join(", ") }}
More examples:
https://www.drupal.org/node/2357633
Filter: simple transformations on output
{{ max(1, 3, 2) }}
{{ random(['apple', 'orange', 'citrus']) }}
More examples:
http://twig.sensiolabs.org/doc/functions/index.html
General Twig functions
Drupal specific functions
Url:
<a href="{{ url('<front>') }}" title="{{ 'Home'|t }}" rel="home" class="site-logo"></a>
Path:
{{ path('entity.node.canonical', {'node': node->id()}) }}
More examples:
https://www.drupal.org/node/2486991
Function: takes arguments to compute output
<header>
...
</header>
<article>
...
</article>
<footer>
{% block footer %}
<h4>This is the footer coming from Template 1!</h4>
{% endblock%}
</footer>
Second template (special.html.twig)
First template (default.html.twig)
{% extends "default.html.twig" %}
{% block footer %}
{{ parent() }}
<h5>But there's a second line now!</h5>
{% endblock%}
{#
/**
* @file
* Theme override to display a block.
*
* Available variables:
* - ...
*
* @see template_preprocess_block()
*/
#}
{%
set classes = [
'block',
'block-' ~ configuration.provider|clean_class,
]
%}
<div{{ attributes.addClass(classes) }}>
{{ title_prefix }}
{% if label %}
<h2{{ title_attributes }}>{{ label }}</h2>
{% endif %}
{{ title_suffix }}
{% block content %}
{{ content }}
{% endblock %}
</div>
block.html.twig
http://slides.com/drubb
http://slideshare.net/drubb
By Boris Böhne
Theming basics & TWIG for Drupal 8 modules, a presentation at Drupal Meetup Stuttgart, 07/02/2015