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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?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; 
	} 
} 
?>