…is gone!
…are being converted to templates…
(and other formats).
Only 12 remaining!
Drupal 7:
/**
* Implements hook_preprocess_HOOK() for node templates.
*/
function MYTHEME_preprocess_node(&$variables) {
$variables['theme_hook_suggestions'][] = 'node__' . 'my_first_suggestion';
$variables['theme_hook_suggestions'][] = 'node__' . 'my_second_more_specific_suggestion';
}
Drupal 8:
/**
* Implements hook_theme_suggestions_HOOK_alter() for node templates.
*/
function MYTHEME_theme_suggestions_node_alter(array &$suggestions, array $variables) {
$suggestions[] = 'node__' . 'my_first_suggestion';
$suggestions[] = 'node__' . 'my_second_suggestion';
}
$variables['list'] = theme('item_list', array(
'items' => $items,
));
$variables['list'] = [
'#theme' => 'item_list',
'#items' => $items,
];
<div{{ attributes }}>
<div {{ attributes }}>
<div✖{{ attributes }}>
<div class="myclass {{ attributes.class }}"{{ attributes|without('class') }}>
<div{{ attributes.addClass('hello').removeClass('goodbye') }}>
{% if attributes.hasClass('field-label-inline') %}
{# Do something special here. #}
{% endif %}
<div{{ attributes.setAttribute('id', 'eye-d') }}>
<div{{ attributes.removeAttribute('id') }}>
// We hide the comments and links
// to print them later.
hide($content['comments']);
hide($content['links']);
print render($content);
// Render calls show() on the element.
print render($content['links']);
// To get back links with the content.
show($content['links']);
// Prints content with links yet
// without comments :(
print render($content);
{# Print without comments and links #}
{{ content|without('comments', 'links') }}
{# Print only links #}
{{ content.links }}
{# Print everything without comment! #}
{{ content|without('comments') }}
{# Print everything YAY :) #}
{{ content }}
Did you notice we snuck that in? 😉
services.yml:
parameters:
twig.config:
debug: true
Example output:
<!-- THEME DEBUG -->
<!-- THEME HOOK: 'block' -->
<!-- FILE NAME SUGGESTIONS:
* block--bartik-powered.html.twig
* block--system-powered-by-block.html.twig
* block--system.html.twig
x block.html.twig
-->
<!-- BEGIN OUTPUT from 'core/modules/block/templates/block.html.twig' -->
<div class="block block-system contextual-region" id="block-bartik-powered" role="complementary">
<div data-contextual-id="block:block=bartik_powered:"></div>
<div class="content">
<span>Powered by <a href="http://drupal.org">Drupal</a></span>
</div>
</div>
<!-- END OUTPUT from 'core/modules/block/templates/block.html.twig' -->
settings.php:
$conf['theme_debug'] = TRUE;
Example output:
<!-- THEME DEBUG -->
<!-- CALL: theme('block') -->
<!-- FILE NAME SUGGESTIONS:
* block--system--powered-by.tpl.php
* block--system.tpl.php
* block--footer.tpl.php
x block.tpl.php
-->
<!-- BEGIN OUTPUT from 'modules/block/block.tpl.php' -->
<div id="block-system-powered-by" class="block block-system">
<div class="content">
<span>Powered by <a href="https://www.drupal.org">Drupal</a></span>
</div>
</div>
<!-- END OUTPUT from 'modules/block/block.tpl.php' -->
hook_theme()
/**
* Implements hook_theme().
*/
function sandwich_theme() {
return [
'sandwich' => [
'variables' => [
'attributes' => [],
'name' => '',
'bread' => '',
'cheese' => '',
'veggies' => [],
'protein' => '',
'condiments' => [],
],
],
];
}
/**
* Builds a sandwich.
*/
public function build() {
return [
'#theme' => 'sandwich',
'#name' => $this->t('Chickado'),
'#attributes' => [
'id' => 'best-sandwich',
'style' => 'float: left;',
'class' => ['left', 'clearfix'],
],
'#bread' => $this->t('Sourdough'),
'#cheese' => $this->t('Gruyère'),
'#veggies' => [$this->t('Avocado'), $this->t('Red onion'), $this->t('Romaine')],
'#protein' => $this->t('Chicken'),
'#condiments' => [$this->t('Mayo'), $this->t('Dijon')],
];
}
Pass in variables using #-prefixed keys.
<section{{ attributes }}>
<h2>{{ name }}</h2>
{% if bread %}
<p><strong>Bread:</strong> {{ bread }}</p>
{% endif %}
<strong>Vegetables:</strong>
<ul>
{% for veg in veggies %}
<li>{{ veg }}</li>
{% endfor %}
</ul>
{% if condiments %}
<strong>Condiments:</strong>
<ul>
{% for condiment in condiments %}
<li>{{ condiment }}</li>
{% endfor %}
</ul>
{% endif %}
</section>
Themable data
drupal_render()
#pre_render
theme()
#post_render
\Drupal::service('renderer')->render()
#pre_render
\Drupal::theme()->render()
#post_render
#type
“Coles Notes”Yes, that's Canadian eh!
/**
* Implements hook_element_info().
*/
function your_module_element_info() {
$types['html_tag'] = array(
'#theme' => 'html_tag',
'#pre_render' => array(
'drupal_pre_render_conditional_comments'
),
'#attributes' => array(),
'#value' => NULL,
);
return $types;
}
/**
* Provides a render element for any HTML tag, with properties and value.
*
* @RenderElement("html_tag")
*/
class HtmlTag extends RenderElement {
public function getInfo() {
return [
'#theme' => 'html_tag',
'#pre_render' => [
[HtmlTag, 'preRenderConditionalComments'],
],
'#attributes' => [],
'#value' => NULL,
];
}
}
Example #type
render array:
$variables['link'] = [
'#type' => 'link',
'#title' => t('Example'),
'#url' => 'http://example.com',
];
Calls #pre_render
to build up it's #markup
from a link generator.
\Drupal::service('link_generator');
$generated_link = $link_generator->generate($element['#title'], $element['#url']->setOptions($options), TRUE);
$element['#markup'] = $generated_link->getGeneratedLink();
{{ 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');
$user->field_first_name = "<script>alert('XSS')</script>";
BAD <?php print $user->field_first_name; ?>
GOOD <?php print check_plain($user->field_first_name); ?>
GOOD {{ user.field_first_name }}
BAD {{ user.field_first_name|raw }}
Evaluate our session:
https://events.drupal.org/node/321
@Cottser
@joelpittet
IRC: #drupal-twig
Twitter: #drupaltwig