Using Cake, once you start working with a large number of data cache files, you see the limitation of storing them all in a single directory (which is the default FileEngine behavior). In my case I have a hierarchical Tree behavior on a model and rather than traverse the tree to find its path each time (which incurs a bunch of DB queries), I simply cache the array of its “branch” to save the work. But this means I have a cache file for each node in the tree, and rapidly that gets to be a big number of files in app/tmp.
Frank de Graaf wrote an excellent drop-in upgrade of FileEngine that lets you name the cache keys as namespaces, which get stored in different directories. So you can, for example, delete a group of keys at once by deleting their directory. He doesn’t recommend it for replacing your default FileEngine cache, but for specialized instances (like mine) it works great. One change I did make to his class was to make it extend FileEngine rather than reimplement it like he does – this results in a very compact class that seems to work just as well, as far as I can tell.
To use it, I chose to activate it at runtime, just where I needed it, then switch back to the default. E.g.,
... function function_that_needs_this_cache_engine() { // Use NamespaceFile cache engine, only for this App::import('Vendor', 'NamespaceFile'); Cache::config('app', array( 'engine' => 'NamespaceFile', 'duration'=> '+1 year', 'prefix' => 'data.' )); Cache::write('namespace.cachename', 'cache_value', "+1 year"); echo Cache::read('namespace.cachename'); // revert to the normal cache engine Cache::config('default', array( 'engine' => 'File', 'path' => ROOT . DS . APP_DIR . DS . 'tmp' )); }
The class is here; put it in app/vendors/namespace_file.php:
<?php/*** Partial reimplementation of Cake's original FileEngine. This* engine supports namespaces similar to:** Cache::write('app.pictures.recent', $recentPictures, 'app');* Cache::write('app.pictures.top', $topPictures, 'app');** This namespacing allows deletion of parent namespaces like:** Cache::delete('app.pictures', 'app');** The settings for this cache should be* defined at the bottom of /config/bootstrap.php like the following:** App::import('Vendor', 'NamespaceFile');* Cache::config('app', array('engine' => 'NamespaceFile', 'duration'=> '+1 hour', 'prefix' => 'cake.'));** Place this file in APP/vendors/.** WARNING: DO NOT USE AS YOUR APP'S DEFAULT CACHE!** @author Frank de Graaf (Phally)* @link http://www.frankdegraaf.net* @license MIT license** // Modified 9/4/10 by echothis - Changed to extend FileEngine, resulting in a much simpler class*/class NamespaceFileEngine extends FileEngine {
/*** Write data for key into cache** @param string $key Identifier for the data* @param mixed $data Data to be cached* @param mixed $duration How long to cache the data, in seconds* @return boolean True if the data was succesfully cached, false on failure* @access public*/function write($key, &$data, $duration) {
$writable =& $this->__setKey($key);
if (!is_a($writable, 'File') || !$this->__init || $data === '') {
return false;
}if (!$writable->exists()) {
$writable->Folder->create($this->__getFolderPath($key));
$writable->create();
}parent::write($key, &$data, $duration);
}/*** Get absolute file for a given key** @param string $key The key* @return mixed Absolute cache file for the given key or false if erroneous* @access private*/function __getPath($key) {
return $this->settings['path'] . str_replace('.', DS, $key);
}/*** Get absolute folder for a given key** @param string $key The key* @return mixed Absolute cache file for the given key or false if erroneous* @access private*/function __getFolderPath($key) {
return $this->__getPath(substr($key, 0, strrpos($key, '.') + 1));
}/*** Return the class (File/Folder) for a key.** @param string $key The key* @return mixed File or Folder class for a key.* @access private*/function __setKey($key) {
return $this->__setPath($this->__getPath($key));
}/*** Return the class (File/Folder) for a path.** @param string $path The path* @return mixed File or Folder class for a path.* @access private*/function __setPath($path) {
$this->__File->Folder->cd($path);
if (is_dir($path)) {
$object = &$this->__File->Folder;
return $object;
}$this->__File->path = $path;
$object = &$this->__File;
return $object;
}/*** Override the parents key method, so the keys don't get transformed** @param string $key The key* @return mixed The key if one is passed else return false* @access public*/function key($key = null) {
if (empty($key)) {
return false;
}return $key;
}}?>
