Ilustración de Lucy Sánchez

Drupal 8: Pseudocampos, templates y render de imágenes con links.

Read this article in english: Drupal 8: Pseudo fields, templates and rendering images with links.

Hace unos días atrás inicie el desarrollo de un nuevo sitio en Drupal 8. Este sitio ha llegado con una gran cantidad de retos interesantes puesto que muchas de las funcionalidades requeridas aún no son sencillas de implementar. En este momento hay muchos módulos contrib que aun se encuentran en desarrollo, uno de estos módulos es: group.

Nuestro cliente requiere que cada usuario autenticado pueda ver los grupos a los que pertenece directamente desde su perfil. Esta funcionalidad tiene que realizase de manera tal que se pudiera ver solamente el logo del grupo, y este logo debe estar enlazado a la página del sitio. Investigué para buscar alternativas con views u otros módulos contrib, pero la investigación no dio muchos frutos.

Opté por el desarrollo de algo custom: elegí desarrollar un pseudocampo para los usuarios. Dicho campo utilizará un template para imprimir dichos grupos.

Manos a la obra.

Primero necesitamos crear un pseudocampo. Recordemos que los pseudocampos son elementos que, al igual que un campo, podemos administrar desde la interfaz de despliegue de cada entidad, pero la diferencia radica en que este elemento solo tiene una función: mostrar información, a diferencia de un campo normal que muestra y almacena información brindada por los usuarios.

En drupal 8- como en drupal 7- necesitamos hacer uso de 2 hooks para hacer disponible nuestro pseudocampo para una entidad, estos son: hook_entity_extra_field_info() y hook_[ENTITY NAME]_view().

hook_entity_extra_field_info().

Indica a la entidad que ahora va a existir un nuevo elemento disponible para su despliegue. Para nuestro caso lo implementamos de la siguiente manera:

/**
* Implements hook_entity_extra_field_info().
*/
function my_module_entity_extra_field_info() {
$extra = [];
$extra['user']['user']['display']['my_mudule_active_subsites'] = array(
'label' => new TranslatableMarkup('Active subsites'),
'description' => new TranslatableMarkup('Show the current subscribed sites by one user'),
);
return $extra;
}

hook_[ENTITY NAME]_view()

En este caso nos interesa usar hook_user_view() porque que estamos trabajando con la entidad de tipo usuario. hook_[ENTITY NAME]_view() básicamente se encarga de manejar el despliegue de cada uno de los elementos de una entidad dada. En este caso lo vamos a utilizar para dar forma a nuestro pseudocampo my_mudule_active_subsites.

Para drupal 8, la implementación de este hook varía un poco en los parámetros que recibe, podemos encontrar más documentación aquí: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Entity%21entity.api.php/function/hook_entity_view/8.2.x

/**
* Implements hook_user_view().
*/
function my_module_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
if ($view_mode === 'full') {
if ($display->getComponent('my_module_active_subsites')) {
$groups = my_module_user_current_user_groups($entity);
$groups_to_render = my_module_render_groups_images($groups);
$build['my_module_user_active_subsites'] = [
'#theme' => 'my_module_active_subsites',
'#groups' => $groups_to_render,
];
}
}
}

Como podemos ver en el ejemplo, estamos usando la variable build que nos llega por referencia para asignar a nuestro pseudocampo el valor a mostrar. Nótese que creamos un nuevo índice dentro del arreglo de build que es un arreglo de render. Dentro de nuestro índice usamos #theme para indicar que vamos a hacer uso de un template, el cual definiremos más adelante y también hacemos uso de #groups que es una variable propia de nuestro template.

Ahora, un detalle importante: como se puede apreciar en nuestro ejemplo, se hace uso de 2 funciones para obtener tanto los grupos del usuario como los datos preprocesados listos para ser impresos por drupal. La función my_module_user_current_user_groups() básicamente retorna un arreglo de entidades de tipo group, por lo cual no ahondaremos en ella. Por el contrario, sí entraremos en detalles con my_module_render_groups_images().

my_module_render_groups_images()

Esta función nació por la necesidad de que nuestras imágenes estén envueltas en un link, específicamente, un enlace a la entidad de tipo grupo. Lo resolvemos de la siguiente manera:

/**
* Helper function to render the images with a specific image style.
*
* @param array $groups
* The entity groups with the images to render.
*/
function my_module_render_groups_images(array $groups) {
$groups_to_render = [];
foreach ($groups as $group) {
$group_item = new Stdclass();
$image_id = $group->field_site_logo->target_id;
if (!empty($image_id)) {
$image_render_array = [
'#theme' => 'image_style',
'#width' => $group->field_site_logo->width,
'#height' => $group->field_site_logo->height,
'#style_name' => 'site_logo_user_profile',
'#uri' => $group->field_site_logo->entity->uri->value,
];
$renderer = \Drupal::service('renderer');
$renderer->addCacheableDependency($image_render_array, $group->field_site_logo->entity);
$rendered_image = $renderer->render($image_render_array);
$link_render_array = [
'#type' => 'link',
'#url' => Url::fromUri('entity:group/' . $group->id()),
'#title' => $rendered_image,
];
$group_item->logo = $link_render_array;
}
$groups_to_render[] = $group_item;
}
return $groups_to_render;
}

Como se puede observar en el ejemplo, lo que realizamos para imprimir nuestra imagen con link, fue: primero preparar un arreglo de render para hacer uso del template de image_style. Luego usamos el servicio renderer de drupal 8 (remplazo de drupal_render en drupal 7) para obtener un objeto de tipo renderer, el cual posteriormente será asignado como valor para el title de nuestro link.

Definiendo nuestro twig template.

Al igual que en drupal 7, si queremos hacer uso de nuestro propio template en un módulo, debemos hacer la definición del mismo, osea, hacerle saber a Drupal que este template existe. Para esto hacemos uso del hook_theme().

/**
* Implements hook_theme().
*/
function my_module_user_theme($existing, $type, $theme, $path) {
return [
'my_module_user_active_subsites' => [
'variables' => [
'groups' => '',
],
],
];
}

Creando nuestro template.

Ahora, como paso final, procederemos a crear nuestro twig template. Para que nuestros templates sean reconocidos debemos colocarlos en una ubicación específica, siguiendo un formato de nombre específico.

Nuestros templates deben estar en una carpeta llamada “templates” en la raíz de nuestro módulo y como mencionamos, deben seguir el siguiente formato de nombre: nombre-template.html.twig. Para nuestro caso creamos el archivo: my-module-active-subsites.html.twig.

Este template se encargará de formatear e imprimir las imágenes pertenecientes a nuestros grupos:

{#
/**
* @file
* Template file my_module_active_subsites field
*/
#}
<div class="container user-groups-container">
{% if groups is not empty %}
{% for group in groups %}
<div class="container user-groups-element" >
{{ group.logo }}
</div>
{% endfor %}
{% endif %}
</div>

Como podemos observar twig es muy distinto de lo que conocíamos antes en los PHP templates, pero esto no significa que sea complicado. En mi caso me apoyé en esta documentación: https://www.drupal.org/docs/8/theming/twig/comparison-of-phptemplate-and-twig-theming-paradigms, esta documentación da una comparación entre lo que antes hacíamos con PHP templates y cómo se hace ahora con twig.

Como pudimos ver, los pseudocampos de Drupal 8 al igual que en Drupal 7, siguen siendo un gran recurso que permite dar flexibilidad a nuestros despliegues de entidades. Si a esto le unimos templates, podemos cumplir con cualquier requerimiento de despliegue de nuestro contenido. Solo se debe recordar que el uso de los mismos depende del tipo del despliegue que queramos usar para nuestro contenido.

¡Feliz codificación!


Manatí es una firma consultora web de origen costarricense, donde hacemos sitios estratégicamente diseñados para llevar organizaciones a lograr objetivos.

No se pierdan los proyectos, noticias e ideas de nosotros en Manatí. Conozca más sobre lo que hacemos, síganos en medium, twitter y facebook.