Render API 

Two parts:

Render pipeline

Render arrays

Render Arrays

  • builduing blocks of a Drupal page
  • structured arrays
  • kept as arrays until the final stage of generating the response

Render arrays are nested and thus form a tree. Consider them Drupal's "render tree" — Drupal's equivalent of the DOM.

What is "rendering" ?

Turning "render" arrays into HTML

What is a render array ?

A structured array that provides data along with hints as to how it should be rendered

$page = [
  '#type' => 'page',
  'content' => [
    'system_main' => […],
    'another_block' => […],
    '#sorted' => TRUE,
  ),
  'sidebar_first' => [
    …
  ],
];

A page render array

$example = [
  '#prefix' => '<div class="hello">',
  '#markup' => 'Hello world!,
  '#suffix' => '</div>',
];
<div class="hello">Hello world!</div>

Example

Render array properties

#type

Set of predefined properties

$example = [
  'link' =>
    '#title' => t('Homepage'),
    '#type' => 'link',
    '#url' => Url::fromRoute('<front>'),
  ],
];
<a href='/'>Homepage</a>

#cache

Very flexible cacheability

#markup

Provides HTML markup directly

If it is too complicated please use #theme or #type instead.

Strips XSS vectors

<a href='/'>Homepage</a>
Welcome to my awesome site !
$example = [
  'link' => [
    '#title' => t('Homepage'),
    '#type' => 'link',
    '#url' => Url::fromRoute('<front>'),
  ],
  'slogan' => [
    '#markup' => t('Welcome to my awesome site !'),
  ],
];

#prefix

#suffix

$example = [
  'link' => [
    '#title' => t('Homepage'),
    '#type' => 'link',
    '#url' => Url::fromRoute('<front>'),
  ],
  'slogan' => [
    '#prefix' => '<h2>',
    '#markup' => t('Welcome to my awesome site !'),
    '#suffix => '</h2>',
  ],
];
<a href='/'>Homepage</a>
<h2>Welcome to my awesome site !</h2>

#pre_render

Alters the array before it is rendered

$example = [
  'link' => [
    '#title' => t('Homepage'),
    '#type' => 'link',
    '#url' => Url::fromRoute('<front>'),
  ],
  'slogan' => [
    '#prefix' => '<h2>',
    '#markup' => t('Welcome to my awesome site !'),
    '#suffix => '</h2>',
    '#pre_render' => array('MyRenderer', 'replaceH2withH3'),
  ],
];
class MyRenderer {

public function replaceH2withH3(array &$build) {
 $build['#prefix'] = '<h3>';
 $build['#suffix'] = '</h3>';
}
<a href='/'>Homepage</a>
<h3>Welcome to my awesome site !</h3>

#post_render

Alters the array after it is rendered

$example = [
  'link' => [
    '#title' => t('Homepage'),
    '#type' => 'link',
    '#url' => Url::fromRoute('<front>'),
  ],
  'slogan' => [
    '#prefix' => '<h2>',
    '#markup' => t('Welcome to my awesome site !'),
    '#suffix => '</h2>',
    '#pre_render' => array('MyRenderer', 'replaceH2withH3'),
    '#post_render'=> array('MyRenderer', 'addDivWrapper'),
  ],
];
class MyRenderer {

...

public function addDivWrapper(array $build, &$output) {
 $output = '<div>' . $output . '</div>';
}
<a href='/'>Homepage</a>
<div><h3>Welcome to my awesome site !</h3></div>

#theme

Takes responsibility of rendering

Knows the structure of the array

$example = [
 ...
  'greeting' => [
    '#theme' => 'my_awesome_array',
    '#name'=> \Drupal::currentUser()->getUsername(),
  ],
];
function hook_theme($existing, $type, $theme, $path) {
  return [
    'my_awesome_array' => [
      'variables' => [
        'name' => NULL
       ],
     ],
  ];
}
<a href='/'>Homepage</a>
<div class="slogan"><h3>Welcome to my awesome site !</h3></div>'
<div class='greeting'>Hello Daniel !</div>
/** my-awesome-array.html.twig */
<div class='greeting'>Hello {{ name }} !</div>

#theme_wrappers

Mostly adds wrappers to rendered children

$example = [
 ...
  '#slogan' => [
    '#theme_wrappers' =>[
      'container' => [
        '#attributes' => 
          '#class' => 'slogan',
      ],
    ],
    ...
  ],
];
<a href='/'>Homepage</a>
<div class="slogan"><h3>Welcome to my awesome site !</h3></div>
<div class='greeting'>Hello Daniel !</div>
$example = [
  'link' => [
    '#title' => t('Homepage'),
    '#type' => 'link',
    '#url' => Url::fromRoute('<front>'),
  ],
  'slogan' => [
    '#prefix' => '<h2>',
    '#markup' => t('Welcome to my awesome site !'),
    '#suffix => '</h2>',
    '#pre_render' => array('MyRenderer', 'replaceH2withH3'),
    '#post_render'=> array('MyRenderer', 'addDivWrapper'),
    '#theme_wrappers' =>[
      'container' => [
        '#attributes' => 
          '#class' => 'slogan',
      ],
    ],
  ],
  'greeting' => [
    '#theme' => 'my_awesome_array',
    '#name'=> \Drupal::currentUser()->getUsername(),
  ],
];
<a href='/'>Homepage</a>
<div class="slogan"><h3>Welcome to my awesome site !</h3></div>
<div class='greeting'>Hello Daniel !</div>

Finally

For more check

Some elements could be unavailable for Render API, but it is the best documentation we got.

Why ? 

Flexibility

Cacheability

How are render arrays related to element types ?

Modules can define new RenderElement plugins.

/** 
 * @RenderElement("fieldset")
 */
class Fieldset extends RenderElement {

  /**
   * {@inheritdoc}
   */
  public function getInfo() {
    $class = get_class($this);
    return array(
      '#process' => array(
        array($class, 'processGroup'),
        array($class, 'processAjaxForm'),
      ),
      '#pre_render' => array(
        array($class, 'preRenderGroup'),
      ),
      '#value' => NULL,
      '#theme_wrappers' => array('fieldset'),
    );
  }

}

Render Pipeline 

Render API

By Popdan Daniel

Render API

  • 651