This week, I’ve been working on a couple of issues in the Drupal cacheability issue queue. In a couple of those issues, I’ve had the opportunity to work with lazy builders. This is a really interesting new concept in drupal 8, and I’m going try to explain a little bit more about how this works here.
We’re going to do this by creating a block that will display a random icecream flavor.
Now, there are 2 options to resolve this issue:
- Make every page with the block on it uncacheable. This is horrible for performance but it’ll work.
This is simple to do:
$build['#cache]['max-age'] = 0;
- The second solution is introducing a placeholder and attaching a lazy builder to it. This is a little bit more complex but it’s the best for cacheability because you can have the rest of the render array be cacheable and only have one part of it being uncacheable.
A lazy builder, is a piece callback function that gets executed every request, so that the surrounding code is can still be cached. This is a way to make sure that all your pages can be cached and keep all the required complexity. First of all, we need to implement a callback for the lazy builder.
So, we’ve created a new class that has one public method, the lazyCallback
method.
This can be as complex as you want, as long as you’re returning a renderable array.
In this example the renderable array is explicitly non-cacheable, but because this is just another render-array, it can have it’s own set of cache-tags / cache-contexts.
If you want to have a different flavor based on every page, but it can be cached per url, the only change needed would be to change the #cache
part of the returned render array.
Now, here’s the block that will use this callback:
You can see in the code, that we’ve set #lazy_builder
as an array containing a string and another array.
If you don’t specify the second array, Drupal will give you an error with the following message: #lazy_builder property must have an array as a value, containing two values: the callback, and the arguments for the callback.
The callback is set as 'icecream:lazyCallback'
.
This works because we’ve defined the class in services.yml as icecream
These are all the pieces that you need to make a #lazy_builder
callback.
You can use this solution every time you have a small portion of a page that’s too complex to be cached.
Lazy builders make sure that the rest of the page is still cacheable, and that can help move quite some load off the server.
If you want to have a look at the complete code, you can check it out on my bitbucket: https://bitbucket.org/botchcake/icecream-lazybuilder/src.
Update - note that the placeholders that are currently being created in the block aren’t going to be needed once https://www.drupal.org/node/2499157 lands.
@Borisson Note that once https://t.co/KPJLqdUZKf lands, you won't have to deal with all that placeholder stuff anymore!
— Wim Leers (@wimleers) August 3, 2015
@Borisson Also, you can already just do #create_placeholder ⇒ TRUE. (The core issues you worked on are edge cases, it's not possible there.)
— Wim Leers (@wimleers) August 3, 2015