Stefan Wanzenried
Software Engineer
Mar 27, 2018 4 min read

Understanding Drupal's Auto-Placeholdering

A practical example how to take advantage of the dynamic page cache.

Hi, I am a Software Engineer working @gridonic in Bern, Switzerland. I am still a Drupal-Newbie and have recently explored the powerful but complex Cache-API of Drupal 8. I hope that this article may help other devs to better understand the concept behind ā€œauto-placeholderingā€.

Drupal uses theĀ Internal page cacheĀ module to cache pages for anonymous users. This is great, because it speeds up our sites noticeably. However, as Drupal developers, we need to pay attention to NOT cache dynamic content. Consider a simple example of displaying a random image on each request. We use a preprocess function in the theme file to randomly select an image and output it via Twig:

Can you guess what happens?

  • The first page request (cache-miss) will display a random image and build the cache šŸ‘

  • Subsequent requests (cache-hits) always display the same imageā€Šā€”ā€Šthe one which got cached with the first request šŸ‘Ž

There are different solutions to overcome this problem:

  1. Use Javascript to render the dynamic part.

  2. Use Ajax to load dynamic parts after the page has been served from cache.

  3. Disable all caching modulesā€Šā€”ā€Šwait, what? šŸ˜±Ā Donā€™t.

  4. Use theĀ Dynamic page cacheĀ in combination withĀ auto-placeholderingĀ to render the dynamic content.

Solution 4Ā for the win! It took me some time to understand the concept behind auto-placeholderingā€Šā€”ā€Šalso because theĀ official documentationĀ does not include any examples. Letā€™s get started with one!

Example: Dynamically show or hide a newsletter block

In a recent project I needed to show or hide a newsletter signup form in the footer of each page depending on the following conditions:

  • Authenticated user: Use the mailchimp API to check if the email address of the current user is registered. If so, hide the newsletter block. Additionally, set a cookie which indicates a subscription. If the user logs out, he or she still wonā€™t see the newsletter block because of the cookie.

  • Anonymous user: Hide the newsletter block if the cookie is present.
    I used theĀ preprocess_region__footerĀ hook in the theme to set a variable for the newsletter:

If you are familiar with Drupalā€™sĀ render-arrays, you will notice that the array being assigned to theĀ newsletterĀ variable looks special. In fact, this is not a render-array. TheĀ #lazy_builderĀ key in combination with the userĀ cache-contextĀ tells Drupal to use auto-placeholdering. The rendering is delegated to the lazy builder callback which will return a render-array at some later point in time. Roughly, it works like this:

  1. During rendering, Drupal assigns a placeholder to theĀ newsletterĀ variable, hence the nameĀ auto-placeholdering.

  2. TheĀ Dynamic page cacheĀ caches the page with this placeholder.

  3. Before the cached response is sent to the user, the placeholder is replaced by the callback of the lazy builder.

This means that Drupal is able to serve the whole page from cache except the placeholders containing the dynamic parts.

The userĀ cache-contextĀ varies the cache by user and is a condition to actually trigger auto-placeholdering. Note that there exist other conditions which tell Drupal to apply this technique (see docs).

Let us take a closer look at the lazy builder:

'#lazy_builder' => [ 'Drupal\my_module\NewsletterRenderer::renderIfSubscribed', [Drupal::currentUser()->getEmail()], ],

The first element of the array references the callback function which returns the render array for the placeholder. The second element is an inner array holding the arguments for the callback function. I decided to use a simple class for my lazy builder callback, it looks like this:

TheĀ renderIfSubscribedĀ method receives the email address of the current user and calls a service to check for the subscription. If not subscribed, it returns a render-array holding the signup form, otherwise just empty markup. The implementation details of the newsletter service are not important, it first performs the cookie check and then uses the mailchimp API to lookup for a subscription.

Cool, but what are the benefits of using this technique?

First of all it guarantees a fast loading website because pages are served from cache, minus the dynamic parts. The response is still fully rendered server-side, there is no need to use Javascript or Ajax to render the dynamic parts (this also avoids DOM flickering). However, it really depends on the website and use-caseā€” you may find that using theĀ Internal page cacheĀ in combination with Javascript for the dynamic parts is just fine.

Preparation to successfully use and test auto-placeholdering

  • Uninstall theĀ Internal page cacheĀ module since this technique only works with the Dynamic page cache module.

  • Local environment: Make sure to enable all caches in yourĀ settings.local.phpĀ file.

  • Debugging:Ā Enable the caching headersĀ and use your browserā€™s inspection tools to check for theĀ X-Drupal-Dynamic-CacheĀ response header. A value of HIT tells you that the page has been served from the cache. If at the same time your dynamic content behaves as expected, congratsā€Šā€”ā€Šyou did it! šŸŽ‰