When your web project takes off and receives a lot of page impressions you’ll want to improve its performance. One way to achieve this is to reduce expensive SQL queries by caching the data your models retreive from the database.
Zend Framework offers a variety of ways to cache data…I’ll explain how you can add a cache to all of your models with a few lines of code.
Zend_Cache_Frontend_Class can be used to cache the results of static and instance methods of classes. We’ll add one such cache object to every model. The cache model could look like this. Please note that this class only caches instance methods.
class TVD_Cache_Model_Base {
private $object;
public $cache;
private $class_methods = array();
public function __construct($object) {
$this->object = $object;
foreach (get_class_methods($object) as $method) {
$this->class_methods[$method] = true;
}
$frontend_name = 'Class';
$frontend_options = array(
'lifetime' => 1800,
'cached_entity' => $object,
'cached_methods' => array('count', 'load'),
);
$backend_name = 'File';
$backend_options = array(
'cache_dir' => '/tmp/cache',
'file_name_prefix' => get_class($this->object),
);
$this->cache = Zend_Cache::factory($frontend_name, $backend_name, $frontend_options, $backend_options);
}
/**
* Sets the tags for a cached method
*
* @param array $tags Tags, with which the data will be cached to make
* invalidating the data easier
*/
public function setTagsArray($tags = array()) {
$this->cache->setTagsArray(array_merge(array(get_class($this->object)), $tags));
}
public function __call($method, $args) {
if($this->class_methods[$method]) {
$data = $this->cache->__call($method, $args);
return $data;
}
throw new TVD_Cache_Model_Base_Exception('Method ' . $method . ' does not exist in this class ' . get_class($this->object) . '.');
}
}
class TVD_Cache_Model_Base_Exception extends Exception {}
Next, you create a class from which all your models inherit and which contains an instance of the cache class:
class TVD_Db_Table extends Zend_Db_Table
{
protected $cache;
public function cache() {
if (! $this->cache) {
$this->cache = new TVD_Cache_Model_Base($this);
}
return $this->cache;
}
public function setCacheTags($tags = array()) {
$class_name = get_class($this);
$tag_list = array($class_name);
$this->cache->setTagsArray(array_merge($tag_list, $tags));
}
}
You can now choose whether or not you want to use the cached method call. Suppose you’ve got a Comments object with a function getLatest() that fetches the latest comments and takes too much time:
class Comments extends TVD_Db_Table
{
public function getLatest() {
// expensive SQL calls that consume a lot of time
}
}
You can now call the uncached and cached methods at your leisure:
$commentsClass = new Comments();
// no cache, fetches data from the database on every call
$data = $commentsClass->getLatest();
// cached, fetches data only once and caches it for how long you tell the cache to do so
$data = $commentsClass->cache()->getLatest();
This is only the start…setting the right tags and invalidating the cache is beyond the scope of this short article but you’ll have to figure that out yourself.