Render API en Drupal 7

10/03/2015

Mejora tu forma de generar marcado usando Render API

Cuando tenemos que renderizar un marcado HTML podemos hacerlo de muchas maneras, pero eso no significa que lo estemos haciendo bien. En este artículo vamos a ver cómo podemos transformar la generación de un marcado desde una forma un poco chapucera a una forma más elegante usando render API. 
Usando render arrays no solo conseguiremos que nuestro código sea más limpio sino que además sea algo más reusable entre otras muchas ventajas.

El código dañino

Si estas leyendo estas líneas seguramente te hayas enfrentado en algún momento de tu carrera profesional a un código de renderizado de contenido que daña la salud solo con verlo. Si no lo has hecho todavía, tranquilo, lo harás.

Lo que vas a ver a continuación es un código zombie o muerto, no apto para menores de 13 años y que conviene tenerlo a distancia, si no me crees, compruébalo tu mismo:

function render_examples_example1() { 
  $output  = '<div class="logged-user-info">';  
  // 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;
}

Este código muestra los puntos de un usuario en un sistema de logros. Tras analizarlo un poco podemos ver varias características como buen código zombie que es: 

  • No reusable:  No se puede reusar en otros puntos de nuestro site, al menos de una forma elegante e inteligente.
  • Muy propenso al ctrl+c/ctrl+v: Ya que no es muy reusable, la única forma de reusarlo es mediante esta antigua técnica. De esta forma lo único que conseguimos es que se propague la plaga.
  • No usa estructuras ya hechas: No usa ningún theme para generar el output necesario, por lo tanto reinventa la rueda. En el caso que necesitásemos pintar una lista de elementos tendríamos que escribir las etiquetas directamente y no usaríamos ninguna capa de abstracción.
  • Los themes no actuarán en tu output ya que no usa themes para los cuales los themes están preparados.

Tratamiento a base de themes

Para intentar adecentar nuestro código vamos a cambiar algunas cosas pero siempre manteniendo la misma salida HTML. Para ello vamos a seguir el siguiente plan de ataque:

  • Sustituir aquellas partes de código por themes que ya nos proporciona Drupal y por lo tanto estandarizamos estas partes.
  • Para aquellas partes que no podemos equiparar con un theme ya definido, declararemos e implementaremos nuestra propia función de theme.

Aplicando estas sencillas reglas mejoramos bastante nuestro código inicial. En nuestro caso vamos a crear una función de theme llamada 'user_achievements' sacando las etiquetas HTML a un template aparte. En este artículo no vamos a entrar en cómo declarar una función de theme propia. Si es la primera vez que implementas tu propia función de theme te recomiendo que eches un vistazo al módulo examples.

Una vez que tenemos claro cómo vamos a atacar a nuestro código zombie y qué armas vamos a usar, es hora de ponerlo en práctica:

function render_examples_example1() {
  $output  = '<div class="logged-user-info">';  
  // User achievements:
  $output .= theme('user_achievements', 
               array('points' => 4,'score' => 16));
  $output .= '</div>';

  return $output;
}

¿Mucho mejor verdad? Simplemente hemos sacado nuestra salida HTML a una función de theme, de esta forma podemos llamarla desde varios puntos de nuestro site, además otros módulos pueden implementar funciones de preprocesado para alterar las variables, etc.

Aunque mejorado, nuestro código sigue presentando algunas carencias:

  • Otros módulos no pueden alterar nuestra salida.
  • No podemos encapsular nuestro contenido con diferentes etiquetas envolturas.
  • Si quisiéramos añadir CSS o JS deberíamos preocuparnos de añadirlo en todas las páginas en donde se necesitase.

Aplicando render arrays 

Como paso final vamos a modificar nuestro código usando render arrays para solucionar los puntos que hemos señalado anteriormente.

function render_examples_example1() {
  $output  = array(
    'user_achievements' => array(
       '#theme' => 'user_achievements',
       '#points' => 4,
       '#score' => 16,
    ),
    '#attributes' => array('class' => array('logged-user-info')),
    '#theme_wrappers' => array('container'),
  );
  return $output;
}

Como puedes ver, hemos pasado de devolver un string con nuestro HTML a devolver un array donde definimos como va a ser nuestro HTML. De esta forma hemos mejorado en los  siguientes puntos:

  • Nuestro código ya es fácilmente alterable por otros módulos, por ejemplo mediante un hook_page_alter.
  • Hemos usado themes_wrappers de tal forma que hemos podido encapsular nuestro contenido de diferentes formas de una forma sencilla.
  • Si necesitasemos añadir CSS o JS podríamos añadiendo la propiedad #attached de tal forma que nuestros CSS o JS se añadirán de forma automática cuando se renderiza nuestro array.

 Conclusiones y referencias

En este artículo hemos visto un ejemplo muy sencillo de cómo mejorar la forma en la que mostramos contenido HTML en Drupal. Si quieres saber más, échale un vistazo a la charla que dí en el grupo local de Drupal Sevilla sobre Render API en drupal 7

Además tenéis los siguientes recursos: