Passing data between different Drupal hooks

Passing data with hooks to twig templates is as easy as it sounds. Passing it between different hooks is another story.

Drupal, hooks are one of the ways to interact with Drupal. Hooks can be used to alter Drupal's core behaviour as well as contrib modules behaviour.

As an example, propably the most used way how Drupal's hooks are used is by adding classes to DOM elements. Hooks are also used for passing down data as variables directly to specific twig templates.

1function THEME_preprocess_html(&$variables) {
2 global $base_secure_url;
3 $variables['theme_path'] = file_create_url($base_secure_url . '/' . drupal_get_path('theme', 'THEME'));
4}

In the first code example, we are passing down the variable theme_path to the template html.html.twig via the THEME_preprocess_html() hook.

1{{ theme_path }}

Now the html.html.twig template can use the theme_path variable we just created by rendering it in the template in parentheses.

Passing data to another hook

Passing variables to twig templates is simple enough and usually doesn't bring any trouble.

But it gets tricky when you need to use specific data, that you can only obtain in a specific hook, but need to use the data in another hook, where you are passing it down to a template.

As an example, you have a paragraph item which uses entity reference items with an unlimited amount. You need to get the entities total amount and pass it down to each entities node template. The problem is you can only obtain the amount of entities in the hook of the paragraph item.

Solution

It took me a while to find a working solution, but luckily I found it at StackOverflow (like any other solution).

The solution is to add your data into an object item, which then can be fetched in the entities hook.

In this example the data we want is in the THEME_preprocess_paragraph() hook.

1use Drupal\node\Entity\Node;
2
3function THEME_preprocess_paragraph(&$variables) {
4 $paragraph = $variables['paragraph'];
5 $paragraphType = $paragraph->bundle();
6
7 switch ($paragraphType) {
8 case 'your_paragraph_id':
9 // Check if the paragraph has your field.
10 if ($paragraph->hasField('your_field')) {
11 // This is an array with 0-n items.
12 $yourField = $paragraph->get('your_field');
13
14 // Here we iterate over every item in the array.
15 foreach ($yourField as $key => $value) {
16 // Get the id from the node and load it.
17 // After that, you'll create new object item
18 // with the node's parent paragraph's data.
19 $nodeId = $value->getValue()['target_id'];
20 $node = Node::load($nodeId);
21 $node->field_parent_paragraph = $paragraph;
22 }
23 }
24
25 break;
26 }
27}

After passing the data down to the entity, the data can be fetched in the THEME_preprocess_node() hook.

1function THEME_preprocess_node(&$variables) {
2 $node = $variables['node'];
3
4 switch ($node->getType()) {
5 case 'your_node_type':
6 // Here we check if the node has the created object item.
7 if (isset($node->field_parent_paragraph)) {
8 $paragraph = $node->field_parent_paragraph;
9 // The field is a ReferencedEntity class, which has handy functions to get data.
10 $paragraphEntities = $paragraph->get('your_field')->referencedEntities();
11 $paragraphEntitiesCount = [];
12
13 // Do whatever you want with the data.
14 // In this case, I want to create an array, which has the node ID's in it.
15 foreach ($paragraphEntities as $key => $node) {
16 array_push($paragraphEntitiesCount, $node->id());
17 }
18
19 // Pass the wanted data to the template.
20 $variables['paragraphEntitiesCount'] = $paragraphEntitiesCount;
21 }
22
23 break;
24 }
25}

Now the data has been passed down to the template where it can be rendered and can be used how you like.

1{% for nodeID in paragraphEntitiesCount %}
2 // Do stuff ...
3{% endfor %}

And we are done!