Drupal 8: Pseudo fields, templates and rendering images with links.

Illustration by Lucy Sánchez

Lea este artículo en español: Drupal 8: Pseudocampos, templates y render de imágenes con links.

A few days ago I started the development of a new Drupal 8 site. This site has come with a large set of interesting challenges because a lot of the required functionality is still not easy to implement. As of this moment, there are a lot of contrib modules that are in development, one of them is group.

Our client requires that every authenticated user can see the groups it belongs to directly from its profile. This feature is required to be done in such a way that you can see the group’s logo, and the logo has to be linked to that group page on the site. I researched alternatives with views and other contrib modules, but it didn’t bear fruit.

I opted for the custom approach: to create a pseudo field for the users. Such field would use a template in order to print the groups the user belongs to.

Shoulders to the wheel!

First we need to create a pseudo field. Let’s remember that pseudo fields are elements that, just like fields, we can manage from the admin interface of each entity, but the difference lies in that the element has only one function: to display information. Contrary to that, a field will store and show information entered by the users.

In Drupal 8 -like Drupal 7- we need to use 2 hooks to create our pseudo field for an entity: hook_entity_extra_field_info() and hook_[ENTITY NAME]_view().

hook_entity_extra_field_info().

It tells the entity that there will be a new element available to display. For our case, we’ll implement it like so:

/**
* 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()

In this case, we’re interested in using hook_user_view() because we’re working with the user type entity. hook_[ENTITY NAME]_view() basically handles the display of each one of the elements of an entity. In this case, we’ll use it to format our pseudo field my_mudule_active_subsites.

For Drupal 8, the implementation of this hook varies a bit in the parameters it receives, we can find more documentation here:

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,
];
}
}
}

Like the example, we’re using the variable build that we get from the reference, to assign the value to show with our pseudo field. Notice that we created a new index inside the build array, which is a render array. Inside our index we’ll use #theme to indicate that we’re going to use a template, which we’ll define further down the road, and we’ll also use a variable called #groups, which is a variable inside our template.

Now, an important detail: like you can see in our example, we use 2 functions to obtain both the the groups of the user and the preprocessed data ready to be printed by Drupal. The function my_module_user_current_user_groups() basically returns an array of the entity type group, in which we wont delve. On the contrary, we’ll see the details of my_module_render_groups_images().

my_module_render_groups_images()

This function was born because of our need to wrap the images in a link, specifically a link to the entity type group. We’ll solve that this way:

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

As you can see in the example, what we do to print our image with a link is: first, prepare a render array to use the image_style template. After that, we use the service renderer from Drupal 8 (instead of Drupal_render from Drupal 7) to obtain an render type object, which will be assigned as a value for the title of our link.

Defining our twig template

Like Drupal 7, if we want to use our own templates inside a module, we need to define it, that is, make Drupal aware that such template exists. For that we’ll use hook_theme().

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

Creating our template

For our final step, we’ll proceed to create our twig template. In order to be recognized, our templates have to be placed in a specific location, with a specific naming convention.

Our templates have to be inside the folder called “templates” in the root of our module and like as I mentioned, have to follow the naming convention: template-name.html.twig. We’ll create the file my-module-active-subsites.html.twig.

This template will handle the format and will print the images belonging to our groups:

{#
/**
* @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>

As you can see, Twig is very different from what we know before with PHP templates, but this does not mean is complicated. In my case, I used this documentation: https://www.drupal.org/docs/8/theming/twig/comparison-of-phptemplate-and-twig-theming-paradigms, which is a comparative between what we used to do in PHP template and how we do it with Twig.

Pseudo fields in Drupal 8 -like in Drupal 7- are still a great resource that allows flexibility in the display of our entities. If we add templates in the mix, we can comply with any display requirement for our content. Just be aware that it depends on the type of display we need to use for our content.

Happy coding!

Manatí is a web consultancy firm from Costa Rica, where we make websites strategically designed to help organization achieve goals.

Don’t miss out on projects, news and ideas from Manatí. Learn about what we do, follow us on medium, twitter and facebook.


Drupal 8: Pseudo fields, templates and rendering images with links. was originally published in MANATI | Agencia Web on Medium, where people are continuing the conversation by highlighting and responding to this story.