Render API en D7 & D8

About me

Jose Luis Bellido

http://www.traslaniebla.com/wp-content/uploads/2012/05/got1.png

El terreno de juego

El código más allá del muro

  global $user;

  $account = user_load($user->uid);
  $output  = '<div class="logged-user-info">';
  // User picture:
  $output .= "<a href='/user/$account->uid'>";
  $output .= "<img src='/sites/default/files/pictures/" . $account->picture->filename ."'>";
  $output .= "</a>";
  // User roles:
  $output .= '<ul>';
  foreach($user->roles as $rid => $role) {
    $output .= '<li>'. $role .'</li>';
  }
  $output .= '</ul>';
  // User achievements:
  $output .= '<div class="user-achievements">';
  $output .= '<span class="user-points">' . t('User points: @points', array('@points' => 4)) . '</span>';
  $output .= '<span class="highlighted-achievement">';
  $output .= t('Highest score: @score', array('@score' => 16));
  $output .= '</span>';
  $output .= '</div>';
  $output .= '</div>';

  return $output;
}

El caminante blanco

El código stark

  global $user;

  $output  = '<div class="logged-user-info">';
  // User picture:
  $output .= theme('user_picture', array('account' => $user));
  // User roles:
  $output .= theme('item_list', array('items' => $user->roles));
  // User achievements:
  $output .= theme('user_achievements', array('points' => 4,'score' => 16));
  $output .= '</div>';

  // Add custom css & js:
  drupal_add_js(drupal_get_path('module', 'render_examples') . '/js/user_achievements.js');
  drupal_add_css(drupal_get_path('module', 'render_examples') . '/css/user_achievements.css');

  return $output;

El código stark

El código Targaryan

Los render arrays

 global $user;

  $render = array(
    'user-picture' => array(
      '#theme' => 'user_picture',
      '#account' => $user,
    ),
    'item-list' => array(
      '#theme' => 'item_list',
      '#items' => $user->roles,
    ),
    'user-achievements' => array(
      '#theme' => 'user_achievements',
      '#points' => 4,
      '#score' => 16,
      '#attached' => array(
        'js' => array(drupal_get_path('module', 'render_examples') . '/js/user_achievements.js'),
        'css' => array(drupal_get_path('module', 'render_examples') . '/css/user_achievements.css'),
      ),
    ),
    '#attributes' => array('class' => array('logged-user-info')),
    '#theme_wrappers' => array('container'),
  );

Introducción a los Render arrays

Render API: ¿Qué es?

  • La base del Theme System de Drupal.
  • Array asociativo jerarquizado.
  • El heredero del sistema de renderizado de Form API.

¿Ventajas?

  • Los datos pueden ser alterados por otros módulos.
  • Cache.
  • Themes envoltura.
  • Más reusable.
  • Uniformamos.

¿Inconvenientes?

  • Periodo de adaptación.
  • Más difícil de debugear.
  • Arrays inmensos. => Divide y vencerás.

Render API & Forms API

  • Forms API usa render API
  • Render() para renderizar casi todo.

Propiedades

  • #type      hook_element_info() 
  • #access
  • #theme_wrappers
  • #pre_render
  • #attached

Más en Form API Reference

Render API & Theming

  • #theme : Función de render
  • Variable theme => #variable
$themes['user_achievements'] = array(
    'path' => drupal_get_path('module', 'render_examples') . '/templates',
    'template' => 'user-achievements',
    'variables' => array(
      'points' => '',
      'score' => '',
    ),
  );

1.- Definimos nuestra función de theme : 

Render API & Theming

    'user-achievements' => array(
      '#theme' => 'user_achievements',
      '#points' => 4,
      '#score' => 16,
      '#attached' => array(
        'js' => array(drupal_get_path('module', 'render_examples') . '/js/user_achievements.js'),
        'css' => array(drupal_get_path('module', 'render_examples') . '/css/user_achievements.css'),
      ),
    ),
$render = array(
  '#markup' => theme('user_achievements', array('points' => 4,'score' => 16)),
);

Definimos nuestro render array

¿Cómo lo haríais?

Exprimiendo Render API

Theme Wrappers

  • Funciones theme de envoltura.

¿Ventajas?

  • Normalizamos y reutilizamos.
  • Acceso a todos los atributos.
  • Se pueden anidar.
  • Nos centramos sólo en el contenido.
  • #prefix #suffix?

¿Inconvenientes?

  • No documentadas las ya existentes.
  • Cuidado! #theme_wrappers!
  • Orden de izquierda a derecha
 '#theme_wrappers' => array('container'),
 '#theme_wrappers' => array('container', 'render_array'),

Crear nuestro theme wrapper

$themes['render_add_span'] = array(
    'render element' => 'element',
  );

1.- Defino el theme (hook_theme())

2.- Implementamos

/**
 * Implements theme_THEME().
 */
function theme_render_add_span(&$variables) {
  $element = $variables['element'];
  $output = '<span class="render-wrapper-span">';
  $output .= $element['#children'];
  $output .= '</span>';
  return $output;
}

3.- Lo añadimos

'#theme_wrappers' => array('container', 'render_add_span'),

Crear nuestro Theme_wrapper

El resultado

Attached Libraries, CSS & JS

  • No más drupal_add_js , drupal_add_css

  • No más en hook_init o sitios ocultos.

  • Sólo se carga cuando lo necesitas.

  • Ejemplo de uso AJAX

'#attached' => array(
        'js' => array(drupal_get_path('module', 'render_examples') . '/js/user_achievements.js'),
        'css' => array(drupal_get_path('module', 'render_examples') . '/css/user_achievements.css'),
      ),
drupal_add_js(drupal_get_path('module', 'render_examples') . '/js/user_achievements.js');
drupal_add_css(drupal_get_path('module', 'render_examples') . '/css/user_achievements.css');
'element_cached' => array(
      '#markup' => '',
      '#pre_render' => array('render_element_cached_pre_render'),
      '#cache' => array(
        'keys' => array('render_examples', 'cache-element'),
        'bin' => 'cache',
        'expire' => time() + 10,
        'granularity' => DRUPAL_CACHE_PER_PAGE | DRUPAL_CACHE_PER_USER,
      ),
    ),

Caching with Render API

1.- Definición

2.- El truco

/**
 * Custom pre_render callback for caching example.
 * @param $element
 * @return mixed
 */
function render_element_cached_pre_render($element) {
  $element['#markup'] = render_elements_expensive();
  $element['#children'] = $element['#markup'];
  return $element;
}

Render API en Drupal 8

  • Pocas diferencias con Drupal 7
  • Podemos definir 3 tipos de elementos
    • #type : Elementos render. (ejm Form elements)
    • #theme : Data of a theme hook.
    • #markup : Para elementos sencillos.
  • ​Mejoras en cache
  • Sistema de plugins para definir render elements.

Render API en Drupal 8

Render API overview https://api.drupal.org/api/drupal/core!modules!system!theme.api.php/group/theme_render/8

Render API en Drupal 8

$renderer = \Drupal::service('renderer');

$config = \Drupal::config('system.site');
$current_user = \Drupal::currentUser();

$build = [
  '#prefix' => '<aside>',
  '#markup' => t('Hi, %name, welcome back to @site!', [
    '%name' => $current_user->getUsername(),
    '@site' => $config->get('name'),
  ]),
  '#suffix' => '</aside>',
  '#cache' => [
    'contexts' => [
      // The "current user" is used above, which depends on the request,
      // so we tell Drupal to vary by the 'user' cache context.
      'user',
    ],
  ],
];

// Merges the cache contexts, cache tags and max-age of the config object
// and user entity that the render array depend on.
$renderer->addCacheableDependency($build, $config);
$renderer->addCacheableDependency($build, \Drupal\user\Entity\User::load($current_user->id()));
?>

Render Elements Drupal 8

  • Render elements son plugins!
  • No se borra hook_element_info().
  • Declaracion con anotaciones
  • Generic elements
    • Extends RenderElement
  • Form input elements
    • FormElement 

Tipos de render elements

https://api.drupal.org/api/drupal/core%21modules%21system%21core.api.php/group/plugin_api/8

Theme_wrappers en Drupal 8

  • Practicamente igual que en Drupal 7
  • Tenemos attributos propios del theme_wrapper!!
array(
  '#theme' => 'image',
  '#attributes' => array('class' => 'foo'),
  '#theme_wrappers' => array(
    'container' => array(
      '#attributes' => array('class' => 'bar'),
    ),
    'custom_container' => array(
      '#attributes' => array('class' => 'baz'),
    ),
  ),
);

https://www.drupal.org/node/2066209

Hagamos recopilatorio

  • No reinventes la rueda: usala!
  • Organiza cómo renderizar tu output.
  • Evita usar theme().
  • Separa en elementos renderables
  • Usa los theme_wrappers para unificar
  • Cachea los elementos con mucha carga.

Resources

Material de esta charla

Fuentes y otros recursos.

 ¿ Preguntas ?

Render API Drupal 7 & 8

By jlbellido

Render API Drupal 7 & 8

Charla sobre Render API en Drupal 7 & 8. Esta sesión tuvo lugar en la DrupalCampSpain Jerez 2015 (http://2015.drupalcamp.es). La charla fue grabada y está disponible en https://vimeo.com/128716914

  • 2,669