Caching Data
Here's a simple way to cache data if the item itself has a lot of logic that bogs the server down...
Let's say you run a batch process and build a catalog item for an e-comm store.
Your 'foreach' would be converted into a batch process, but for sake of explaining caching here, we'll assume that a 'foreach' loop wouldn't timeout. :)
foreach ($products as $product) {
// Again, do not use a foreach for large data sets.
generateProductTile($product);
}
/**
* Generates product tiles with a lot of logic behind how they are displayed
*/
function generateProductTile($product) {
$drupalCache = \Drupal::cache();
$cid = 'my_module:product_tile: . ' $product->id;
// If we've already cached this result, we don't have to do any
// excessive processing.
if ($cached = $drupalCache->get($cid)) {
if (!empty($cached)) {
return $cached->data;
}
}
// If we don't have a cache object, we do whatever we need to build the output.
$title = $product->label();
$price = $product->price();
// Maybe we add some logic to the price, and this process is pretty intensive.
$price = change_price($price);
// Maybe we need to get an image... e-comm entities have weird ways of storing images
// so this may also take a while.
$image = get_product_image($product);
// Let's pretend we're done... now we can render this tile.
$themedTile = [
'#theme' => 'product_tile',
'#data' => [
'title' => $title,
'price' => $price,
'image' => $image
]
];
$rendererService = \Drupal::service('renderer');
// This is just a basic way to render the basic html for this item
// based on a hook_theme entry.
$renderedTile = rendererService->renderRoot($themedTile);
// Now that we did all that work, lets cache it so if we run this again
// we don't have to do all that logic again. We can just gather pre-rendered tiles.
// Setting the tag implies that when you save or update a product entity
// this cache will be invalidated and rebuild itself the next time it's called.
// (Double check the tag format for the entity)
$drupalCache->set(cid: $cid, data: $renderedTile, tags: ['product:' . $product->id()]);
// In the ->set method, you can set the expiration as well. By default it is 'permanent'
// If you want some other caching policy, do that.
// Since we want the built item, return it.
return $renderedTile;
}
As an added bonus, here's the hook_theme in the .module file, and the twig format to render this.
<?php
/**
* Implements hook_theme().
*/
function MY_MODULE_theme() {
return [
'product_tile' => [
'variables' => ['data' => NULL],
'template' => 'product_tile' /* This assumes the file is in the /templates directory within your module */
]
];
}
And your twig file in MY_MODULE/templates directory.
** the image could also be pre-rendered, but this is more to show how you can use parts of variables as data within your twig file.
<h1>{{ data.title }}</h1>
<div class="price">{{ data.price }}</div>
<img src="{{ data.image.src }}" alt="{{ data.image.alt }}">