Now that we have looked at the three main cache properties, we need to consider creating render arrays, so let's revisit some of our previous work and apply this in practice as needed.
The render array we will look at is inside the HelloWorldSalutation::getSalutationComponent() service and is used to render the salutation message. We are building it quite dynamically, but a simplified version looks like this (omitting some things):
$render = [ '#theme' => 'hello_world_salutation', '#salutation' => [ '#markup' => $salutation ] ];
Here, $salutation is either the message from the configuration object or the one generated based on the time of day.
Right off the bat, I will mention that this is one of those cases in which we cannot really cache the render array due to its highly dynamic nature. This is caused by the dependency on the time of day. Sure, we could set a maximum age of a few seconds or an hour, but is it even worth it? And we also run the risk of showing the wrong salutation.
So in this case, what we can do is add a maximum age of 0:
$render = [ '#theme' => 'hello_world_salutation', '#salutation' => [ '#markup' => $salutation ], '#cache' => [ 'max-age' => 0 ] ];
The cache metadata goes under a #cache render array property as shown above.
Specifying the max-age basically tells Drupal not to ever cache this render array. Something important to know about this is that this declaration will bubble up to the top-level render array that makes the Controller response, preventing the entire thing from being cached. So, do not make the decision to prevent caching lightly. In our example, this is basically the entire Controller response and it is actually a very simple calculation, so we are good. Later in the chapter, we will talk about the ways this can be mitigated.
Let's, for a minute, assume that our salutation component is simply rendering the message stored in the configuration object and does not show time-specific content. If you remember:
$config = $this->configFactory->get('hello_world.custom_salutation'); $salutation = $config->get('salutation');
In this case, we could cache the render array, but as we discussed earlier, we'd need to think about the dependencies as well as the potential variations it can have. It is already pretty obvious what the dependencies are—the configuration object. So, we would do the following:
$render = [ '#theme' => 'hello_world_salutation', '#salutation' => [ '#markup' => $salutation ], '#cache' => [ 'tags' => $config->getCacheTags() ] ];
Basically, we are requesting this particular configuration object's cache tags and setting those onto the render array. If we had more sets of cache tags to set from multiple objects, we would have to merge them. There is a tool we can use to ensure we do it right. For example:
$tags = Cache::mergeTags($config_one->getCacheTags(), $config_two->getCacheTags());
This will merge two arrays of cache tags, pure and simple. The Drupal\Core\Cache\Cache class also has static helper methods for merging cache contexts and max-ages (among other things, I encourage you to check this out as you progress).
Thankfully, our render array is simple and does not vary, and hence we don't need cache contexts. If, however, we had appended the current username to the salutation, we would have had to add the user context to the render array as follows:
'#cache' => [
'tags' => $config->getCacheTags(),
'contexts' => ['user']
]
This would have cached the render array differently for each user who visits the page and would serve them accordingly at subsequent visits.