I was troubleshooting a preprocess function for a paragraph entity the other day and discovered that the preprocess function was being called four times, even though there was only one instance of this particular paragraph on the page. This particular preprocess function had some process heavy logic in it, so I didn't want to introduce more lag for the process, four times was unacceptable... I dove in...
Looking through the paragraph's specific twig function, I found the following at the top of the file:
{% set cache_bust = content|render %}
Then, further down in the twig, specific fields were being printed along with the markup
<div>
{{ content.field_name }}
</div>
The {{ content }} variable was not being printed explicitly, just specific fields.
So, I removed that first line and, my preprocess function went from being called four times to being called two times... progress.
I dug into the theme some more and discovered that the `page.html.twig` file was calling `content|render` in a similar fashion. So, I commented that line out and success! My preprocess function was being called once, which is what I expected.
What was happening
Every time the `|render` filter was being called within twig, everything in the render array was being re-rendered, thus causing all preprocess functions to run again. In the case above, the rendering of a paragraph was happening four times because:
- Drupal default renders all the render arrays
- There was a call to `|render` in page.html.twig,
- There was a call to `|render` in paragraph.html.twig
- The fourth call was because the `|render` in page.html.twig called the one in paragraph.html.twig.
Why was the |render being used
I inquired with some frond end developers and found out that the |render was being added because, without it, weird cache bugs were occurring and specific field placements were not always showing up when Drupal's caching was enabled.
How to fix the caching issues
The reason there were cache related issues is because the relevant cache metadata was not being printed to the page. As such, content changes were not being reflected.
To fix this, we ended up calling `content|cache_metadata`, which is provided by the twig_tweak module. We almost always use twig tweak, so this worked for us.
If you're not using twig tweak, you need to remember to print the full {{ content }} variable. If you're placing specific fields, you can use the `content|without('field_name') filter to ensure the cache metadata is printed.