Drupal 8
Theme System

hook_theme() to
Twig template

Scott Reeves & Joël Pittet

Scott Reeves

Scott Reeves
@Cottser
  • Developer at Digital Echidna
  • Drupal 8 theme system
    co-maintainer
  • Drupal core mentor
  • Likes beans

Joël Pittet

  • Building Websites since 2001ish
  • Front and Back End Developer
  • Drupal 8 Core Theme System Maintainer
  • And obviously love perogies
Joël Pittet
@joelpittet

Drupal 8
theme layer changes

Template process layer

…is gone!

Theme functions

…are being converted to Twig templates.

Theme suggestion hooks

Drupal 7:


<?php
/**
 * 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:


<?php
/**
 * 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';
}
						

Goodbye theme(), hello
render arrays

Drupal 7:

<?php
$variables['list'] = theme('item_list', array(
  'items' => $items,
));

Drupal 8:

<?php
$variables['list'] = array(
  '#theme' => 'item_list',
  '#items' => $items,
);

Oh yeah, and Twig!

settings.php:

$settings['twig_debug'] = TRUE;

Example output:


<!-- THEME DEBUG -->
<!-- CALL: _theme('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' -->
					

Sandwiches.

https://github.com/DrupalTwig/sandwich

Define using hook_theme()


<?php
/**
 * Implements hook_theme().
 */
function sandwich_theme() {
  return array(
    'sandwich' => array(
      'variables' => array(
        'attributes' => array(),
        'name' => '',
        'bread' => '',
        'cheese' => '',
        'veggies' => array(),
        'protein' => '',
        'condiments' => array(),
      ),
      'template' => 'sandwich',
    ),
  );
}
						

Build your render array


<?php
/**
 * Builds a sandwich.
 */
public function build() {
  return array(
    '#theme' => 'sandwich',
    '#name' => $this->t('Chickado'),
    '#attributes' => array(
      'id' => 'best-sandwich',
      'style' => 'float: left;',
      'class' => array('left', 'clearfix'),
    ),
    '#bread' => $this->t('Sourdough'),
    '#cheese' => $this->t('Gruyère'),
    '#veggies' => array($this->t('Avocado'), $this->t('Red onion'), $this->t('Romain')),
    '#protein' => $this->t('Chicken'),
    '#condiments' => array($this->t('Mayo'), $this->t('Dijon')),
  );
}
						

Pass in variables using #-prefixed keys.

Markup your Twig template


<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>Vegetables:</strong>
    <ul>
      {% for veg in veggies %}
        <li>{{ veg }}</li>
      {% endfor %}
    </ul>
  {% endif %}

  {% if condiments %}
    <strong>Condiments:</strong>
    <ul>
      {% for condiment in condiments %}
        <li>{{ condiment }}</li>
      {% endfor %}
    </ul>
  {% endif %}
</section>
						

Voilà!

Rendered Chickado Sandwich

Overview of rendering flow

  1. drupal_render()
    1. #pre_render
    2. _theme()
      1. Theme suggestion hooks
      2. Preprocess functions
      3. Template is rendered
    3. #post_render

Twig magic

{{ sandwich.cheese }}


// Array key.
$sandwich['cheese'];
// Object property.
$sandwich->cheese;
// Object method.
$sandwich->cheese();
// Object get method convention.
$sandwich->getCheese();
// Object is method convention.
$sandwich->isCheese();
// Object dynamic object property is set and get property.
$sandwich->__isset('cheese');
$sandwich->__get('cheese');

Attributes

All the attributes:
<div{{ attributes }}>
Splitting out the class attribute:
<div class="myclass {{ attributes.class }}"{{ attributes|without('class') }}>

Thanks!

@Cottser

@joelpittet

drupaltwig.org

IRC: #drupal-twig

Twitter: #drupaltwig