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;
}
}
?>