I'm working on a Drupal project that requires calculating some custom stats for each registered user. To get it working, I ran my SQL queries right in hook_user_load(), which is wildly inefficient; it's much better to run the queries once and cache the results for a while. Drupal has nice cache functions built-in, and I thought I should use them, but wasn't totally clear on how to do it based on the documentation.

Jeff Eaton of Lullabot wrote a really helpful article on how to do basic caching in Drupal 7, which is a good introduction, but didn't really explain how to cache a bunch of related, but separately calculated, pieces of data using a function you'd call repeatedly. Examples module is also great, and does include a cache_example module, but also didn't have an example that was right up my alley.

So, here's how to write a single function that can be called multiple times on the same kind of object. This is not what you'd want to do for users or other built-in entities (for those, you should use Entity Cache module). Thanks to Planet Drupal readers for the feedback and help clarifying this.

What follows is a skeleton function from a module we'll call mymodule. Here are the highlights of this function that are a little different than what Jeff's article showed:

Instead of using just __FUNCTION__ as my key in drupal_static() , I'm adding on the object id ($object->id). drupal_static() will staticly cache values during a page load, and I want to make sure that each loaded object is correct if many are loaded during a single page load. Otherwise they could all get the same results!

as my key in , I'm adding on the object id ($object->id). will staticly cache values during a page load, and I want to make sure that each loaded object is correct if many are loaded during a single page load. Otherwise they could all get the same results! I'm setting a unique cache ID ($cid) for each cached item. Looking at the structure of Drupal's cache table, I saw that many modules use the pattern of modulename:type_of_data:id to name their cids, so that's what I used here.

And here's the actual function. The MySQL queries are left out, on the assumption that you'll fill them in with whatever you need.

<?php function _mymodule_get_stats($object) { // Instead of just __FUNCTION__, since this can be called many times per page load, // on a different user each time $object_stats = &drupal_static(__FUNCTION__ . $object->id); if (!isset($object_stats)) { // cid pattern - modulename:datatype:id $cid = 'mymodule:stats:' . $object->id; if ($cache = cache_get($cid)) { $object_stats = $cache->data; } else { // Do expensive stuff. In this case, several MySQL queries $object_stats = array(); // TODO - Fill in with what you need $result1 = db_query(); if ($result1) { $record = $result1->fetchAssoc(); $object_stats['key'] = $record['key']; } $result2 = db_query(); if ($result2) { $record = $result2->fetchAssoc(); $object_stats['key'] = $record['key']; } // keep these stats cached for at least 15 minutes (900 seconds) cache_set($cid, $object_stats, 'cache', time() + 900); } } } ?>