Lauri Eskola (lauriii)
Twitter: @laurii1
Twitter: @laurii1
Themes:
Base themes:
<?php
/**
* Implements hook_theme().
*/
function sandwich_theme() {
return [
'sandwich' => [
'variables' => [
'attributes' => [],
'name' => '',
'bread' => '',
'cheese' => '',
'veggies' => [],
'protein' => '',
'condiments' => [],
],
],
];
}
<?php
public function build() {
return [
'#theme' => 'sandwich',
'#name' => $this->t('Chickado'),
'#attributes' => [
'id' => 'best-sandwich',
],
'#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'),
],
'#attached' => [
'library' => [
'sandwich/flavour'
]
],
];
}
node.html.twig
node--article.html.twig
<?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) {
}
sandwich--chickado.html.twig
sandwich--yummy.html.twig
sites/default/services.yml
parameters:
twig.config:
debug: true
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'node' -->
<!-- FILE NAME SUGGESTIONS:
* node--article--teaser.html.twig
* node--article.html.twig
* node--teaser.html.twig
x node.html.twig
-->
<!-- BEGIN OUTPUT from 'core/themes/bartik/templates/node.html.twig' -->
Will result into this:
<?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';
}
<section{{ attributes.addClass('sandwich') }}>
<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>
Render system
<?php
public function build() {
return [
'#type' => 'sandwich',
'#name' => $this->t('Chickado'),
'#attributes' => [
'id' => 'best-sandwich',
],
'#cheese' => $this->t('Gruyère'),
'#veggies' => [
$this->t('Avocado'),
$this->t('Red onion'),
],
'#protein' => $this->t('Chicken'),
];
}
<?php
/**
* Provides a render element for displaying some delicious sandwiches.
*
* @RenderElement("sandwich")
*/
class Sandwich extends RenderElement {
public function getInfo() {
return [
'#theme' => 'sandwich',
'#attached' => [
'library' => [
'sandwich/flavour'
],
],
'#bread' => $this->t('Sourdough'),
'#condiments' => [
$this->t('Mayo'),
$this->t('Dijon'),
],
];
}
}
<?php
/**
* Provides a render element for displaying some delicious sandwiches.
*
* @RenderElement("sandwich")
*/
class Sandwich extends RenderElement {
public function getInfo() {
return [
'#theme' => 'sandwich',
'#name' => '',
'#pre_render => [
[$this, 'preRenderSandwich'],
],
];
}
public static function preRenderSandwich($element) {
if ($element['#name'] == 'chickado') {
$element['#condiments'][] = t('Mustard');
}
return $element;
}
}
<?php
/**
* Implements hook_element_info_alter().
*/
function sandwich_element_info_alter(array &$types) {
// Decrease the default size of textfields.
if (isset($types['sandwich'])) {
$types['sandwich']['#pre_render'][] = 'sandwich_pre_render_callback';
}
}
function sandwich_pre_render_callback($element) {
...
return $element;
}
{{ 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');
{{ kint(node) }}
Meant to manipulate a variable. Takes the first parameter from the variable before "|".
{% set text = 'Kitten' %}
{# Print variable using length filter. #}
{{ text|length }}
Example
Returns
6
Functions in Twig are like PHP functions
{{ attach_library('color/drupal.color') }}
Example
{# 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);
page.html.twig
...
{% block title %}
<h2{{ title_attributes }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h2>
{% endblock %}
...
page--front.html.twig
{% extends "@classy/page.html.twig" %}
{% block title %}
<h1{{ title_attributes }}>
<a href="{{ url }}" rel="bookmark">{{ label }}</a>
</h1>
{% endblock }
{% import _self as elements %}
{% if items|count > 1 %}
<div>
{{ elements.list(items) }}
</div>
{% else %}
<span>
{{ elements.list(items) }}
</span>
{% endif %}
{% macro list(items) %}
{% if items %}
{% for item in items %}
<div>{{ item }}</div>
{% endif %}
{% endif %}
{% endmacro %}
Rule #1
<?php
use Drupal\Component\Utility\Html
print Html::escape('<em>Kittens</em>');
Prints markup in HTML
<em>Kittens</em>
<em>Kittens</em>
Prints in the browser
{{ text }}
Prints markup in HTML
<em>Kittens</em>
<em>Kittens</em>
<?php
function bartik_preprocess_page(&$variables) {
$variables['text'] = '<em>Kittens</em>';
}
Prints in the browser
{{ text }}
Prints markup in HTML
<em>Kittens</em>
Kittens
<?php
function bartik_preprocess_page(&$variables) {
$variables['text']['#markup'] = '<em>Kittens</em>';
}
Prints in the browser
{{ text }}
Prints markup in HTML
<em>Kittens</em>
Kittens
<?php
use Drupal\Component\Render\FormattableMarkup;
function bartik_preprocess_page(&$variables) {
$variables['text'] = new FormattableMarkup('<em>@txt</em>', ['@txt' => 'Kittens']);
}
Prints in the browser
<?php
use Drupal\Component\Render\FormattableMarkup;
new FormattableMarkup('<em@txt></em>', ['@txt' => 'Kittens']);
new FormattableMarkup('<a href="@url"></a>', ['@url' => 'http://kittens.com']);
new FormattableMarkup('<a href="@url"></a>', [
'@url' => 'javascript:alert(String.fromCharCode(88,83,83))'
]);
These are all dangerous:
<?php
use Drupal\Component\Render\FormattableMarkup;
new FormattableMarkup('<a href=":url"></a>', [
':url' => 'javascript:alert(String.fromCharCode(88,83,83))'
]);
This is safe:
<?php
use Drupal\Core\StringTranslation\TranslatableMarkup;
// These two do the same thing.
$text = new TranslatableMarkup('Translate me');
$text = t('Translate me');
$array[$text] = $text;
<?php
use Drupal\Core\StringTranslation\TranslatableMarkup;
$text = new TranslatableMarkup('Translate me');
$array[(string) $text] = $text;
This will fatal:
This works:
Which means using any custom templating engine or Theme functions are not autoescaped
https://www.drupal.org/node/2575199
Twitter: @laurii1