├── .gitignore ├── Lib └── Error │ └── Exception │ └── AutocacheException.php ├── Model ├── Behavior │ └── AutocacheBehavior.php └── Datasource │ └── AutocacheSource.php ├── README.markdown ├── Test └── Case │ └── Model │ └── Behavior │ └── AutocachePluginTest.php └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject 2 | .idea 3 | -------------------------------------------------------------------------------- /Lib/Error/Exception/AutocacheException.php: -------------------------------------------------------------------------------- 1 | cache_config_name_check - determines if we bother checking if the supplied 65 | // cache configuration name is valid - prevents the developer thinking they are 66 | // caching when they are not - will throw a cache expection if fails this check 67 | // 68 | // > cache_config_name_default - is the default cache name, which by default is 69 | // the string "default" - confused? You just need to make sure you have an 70 | // appropriate Cache::config('default',array(...)) in your bootstrap.php 71 | // or core.php 72 | // 73 | // > dummy_datasource - name of the dummy data source in the database.php file 74 | // should look something like this:- 75 | // public $autocache = array('datasource' => 'AutocacheSource'); 76 | 77 | $this->runtime = array_merge(array( 78 | 79 | // check if the named cache config is loaded 80 | 'cache_config_name_check' => ( Configure::read('debug') > 0) ? true : false, 81 | 82 | // default cache *config* name if no cache name is provided by developer 83 | 'cache_config_name_default' => 'default', 84 | 85 | // name of the autocache dummy datasource *config* name 86 | 'dummy_datasource' => 'autocache', 87 | 88 | ), (array) $config); 89 | } 90 | 91 | /** 92 | * beforeFind 93 | * 94 | * @param Model $model 95 | * @param array $query 96 | */ 97 | public function beforeFind(Model $model, $query) { 98 | 99 | // Provides a place in the Model that we can use to find out what 100 | // autocache did on the last query 101 | $model->autocache_is_from = false; 102 | 103 | // Determine if we are even going to try using the cache 104 | if (!isset($query['autocache']) || ($query['autocache'] === false)) { 105 | return true; // return early as we have nothing to do 106 | } 107 | 108 | // Do the required cache query setup 109 | $this->_doCachingRuntimeSetup($model, $query); 110 | 111 | // Load cached results if they are available 112 | $this->_loadCachedResults($model); 113 | 114 | // Return the cached results if they exist 115 | if ($this->__cached_results) { 116 | 117 | // Note the original useDbConfig 118 | $this->runtime['real_datasource'] = $model->useDbConfig; 119 | 120 | // Check if a DATABASE_CONFIG has been made for the dummy_datasource 121 | // if not establish one based on standard naming 122 | $database_config = &ConnectionManager::$config; 123 | if (!isset($database_config->{$this->runtime['dummy_datasource']})) { 124 | $datasource_name = (string) $this->runtime['dummy_datasource']; 125 | $database_config->$datasource_name = array( 126 | 'datasource' => str_replace('Behavior', '', get_class($this)) . '.AutocacheSource', 127 | 'database' => null 128 | ); 129 | } 130 | 131 | // Use a dummy database connection to prevent any query 132 | $model->useDbConfig = $this->runtime['dummy_datasource']; 133 | } 134 | 135 | return $query; 136 | } 137 | 138 | /** 139 | * afterFind 140 | * 141 | * @param Model $model 142 | * @param array $results 143 | */ 144 | public function afterFind(Model $model, $results, $primary = false) { 145 | 146 | // Check if we obtained cached results 147 | if ($this->__cached_results) { 148 | 149 | // reset the useDbConfig attribute back to what it was 150 | $model->useDbConfig = $this->runtime['real_datasource']; 151 | unset($this->runtime['real_datasource']); 152 | 153 | // A flag to indicate in the Model if the last query was from cache 154 | $model->autocache_is_from = true; 155 | 156 | // return the cached results 157 | return $this->__cached_results; 158 | } 159 | 160 | // Cache the result if there is a config defined 161 | if (isset($this->runtime['config']) && !isset($this->runtime['flush'])) { 162 | Cache::write($this->runtime['name'], $results, $this->runtime['config']); 163 | } 164 | 165 | return $results; 166 | } 167 | 168 | /** 169 | * afterSave Callback 170 | * 171 | * Invalidates the cache for this runtime configuration name 172 | * 173 | * @param Model $model Model the callback is called on 174 | * @param boolean $created Whether or not the save created a record. 175 | * @return void 176 | */ 177 | public function afterSave(Model $model, $created, $options = array()) { 178 | 179 | Cache::clear(false, $this->runtime['config']); 180 | 181 | } 182 | 183 | /** 184 | * afterDelete Callback 185 | * 186 | * Invalidates the cache for this runtime configuration name 187 | * 188 | * @param Model $model Model the callback was run on. 189 | * @return void 190 | */ 191 | public function afterDelete(Model $model) { 192 | 193 | Cache::clear(false, $this->runtime['config']); 194 | 195 | } 196 | 197 | /** 198 | * _doCachingRuntimeSetup 199 | * 200 | * @param Model $model 201 | * @param array $query 202 | */ 203 | protected function _doCachingRuntimeSetup(Model $model, &$query) { 204 | 205 | // Treat the cache config as a named cache config 206 | if (is_string($query['autocache'])) { 207 | $this->runtime['config'] = $query['autocache']; 208 | $this->runtime['name'] = $this->_generateCacheName($model, $query); 209 | 210 | // All other cache setups 211 | } else { 212 | 213 | // Manage the cache config 214 | if (isset($query['autocache']['config']) && !empty($query['autocache']['config'])) { 215 | $this->runtime['config'] = $query['autocache']['config']; 216 | } else { 217 | $this->runtime['config'] = $this->runtime['cache_config_name_default']; 218 | } 219 | 220 | // Manage the cache name 221 | if (isset($query['autocache']['name']) && !empty($query['autocache']['name'])) { 222 | $this->runtime['name'] = $query['autocache']['name']; 223 | } else { 224 | $this->runtime['name'] = $this->_generateCacheName($model, $query); 225 | } 226 | } 227 | 228 | // Check the cache config really exists, else caching will silently not occur 229 | if ( 230 | $this->runtime['cache_config_name_check'] && 231 | !Configure::read('Cache.disable') && 232 | empty(Cache::settings($this->runtime['config'])) 233 | ) { 234 | throw new AutocacheException('Attempting to use undefined cache configuration named "' 235 | . $this->runtime['config']. '"'); 236 | } 237 | 238 | // Cache flush control 239 | if (isset($query['autocache']['flush']) && $query['autocache']['flush'] === true) { 240 | $this->runtime['flush'] = true; 241 | } 242 | } 243 | 244 | /** 245 | * _generateCacheName 246 | * 247 | * @param Model $model 248 | * @param array $query 249 | */ 250 | protected function _generateCacheName(Model $model, $query) { 251 | 252 | if (isset($query['autocache'])) { 253 | unset($query['autocache']); 254 | } 255 | 256 | // NOTE #1: we include the APP as a part of the generated name since it is possible to 257 | // have more than one CakePHP site running on the same webserver and thus it possible 258 | // to have the same query among them - learnt this the hard way - NdJ 259 | // 260 | // NOTE #2: we use json_encode because it is faster than php serialize() 261 | 262 | return $this->cachename_prefix . '_' . 263 | strtolower($model->findQueryType) . '_' . 264 | strtolower($model->alias) . '_' . 265 | md5(APP . json_encode($query)); 266 | } 267 | 268 | /** 269 | * _loadCachedResults 270 | */ 271 | protected function _loadCachedResults(Model $model) { 272 | 273 | $this->__cached_results = false; 274 | 275 | // Flush the cache if required 276 | if (isset($this->runtime['flush']) && true === $this->runtime['flush']) { 277 | Cache::delete($this->runtime['name'], $this->runtime['config']); 278 | } else { 279 | // Catch the cached result 280 | $this->__cached_results = Cache::read($this->runtime['name'], $this->runtime['config']); 281 | } 282 | } 283 | 284 | /** 285 | * _generateGroupName 286 | * 287 | * @param Model $model 288 | * @param array $query 289 | */ 290 | protected function _generateGroupName(Model $model) { 291 | 292 | // Cache grouping - Unfortunately (as at CakePHP 2.5.7) it appears impossible to correctly 293 | // define cache groups after an initial call to Cache::config(). While it is possible to 294 | // workaround this by implementing per-group configs, however if you do that calls to 295 | // Cache::clear() then appear to cause subsequent calls to Cache::write() to not be work as 296 | // expected - more time required 297 | 298 | return $this->cachename_prefix . '_' . 299 | $this->runtime['config'] . '_' . 300 | strtolower(str_replace('/','',APP)) . '_' . 301 | strtolower($model->alias) ; 302 | } 303 | 304 | } 305 | -------------------------------------------------------------------------------- /Model/Datasource/AutocacheSource.php: -------------------------------------------------------------------------------- 1 | true parameter to your Model query. This plugin follows 6 | on from CakephpAutocacheBehavior on Mark Scherer's (https://github.com/dereuromark) 7 | suggestions, thanks Mark! 8 | 9 | The CakephpAutocachePlugin PHPUnit tests have been confirmed against:- 10 | - cakephp-2.6.0-RC1 11 | - cakephp-2.5.7 12 | - cakephp-2.4.10 13 | - cakephp-2.3.10 14 | - cakephp-2.2.9 15 | 16 | NB: CakephpAutocachePlugin uses Cache grouping functionality which only became 17 | available in CakePHP version 2.2 - Groups are defined on a per Model basis hence 18 | changes to a Model will invalidate all other cached data for the same Model 19 | 20 | Download 21 | -------- 22 | - https://github.com/verbnetworks/CakephpAutocachePlugin 23 | 24 | NB: this repo has been moved (2018-07) from its original location at:- 25 | - https://github.com/ndejong/CakephpAutocachePlugin 26 | 27 | Install 28 | ------- 29 | 30 | ### Step 1 - via github.com 31 | Copy or symlink CakephpAutocachePlugin into a path named Autocache in your Plugin 32 | path like this:- 33 | 34 | app/Plugin/Autocache 35 | 36 | Take careful note of the pathname, the name is "Autocache", not AutocachePlugin 37 | or CakephpAutocachePlugin, it's just Autocache. I spell this out because it's 38 | an easy thing to trip up on especially if your pulling this down from github or 39 | unpacking from a tarball. 40 | 41 | ### Step 1 - via composer 42 | Add the following to the require section of your composer.json:- 43 | "verbnetworks/cakephp-autocache-plugin": "dev-master" 44 | 45 | ### Step 2 46 | Make sure you have at least one standard CakePHP cache configuration setup in 47 | core.php or bootstrap.php. You can call your first cache configuration 'default' 48 | and just set it up as a File based cache, like this:- 49 | 50 | Cache::config('default', array('engine' => 'File')); 51 | 52 | ### Step 3 53 | While you have bootstrap.php open, tell Cake to load the plugin like this:- 54 | 55 | CakePlugin::load('Autocache'); 56 | 57 | ### Step 4 58 | Tell your model(s) they $actsAs Autocache by adding this to the top part of the 59 | model definition you want Autocache enabled for. Alternatively you could just put 60 | this into the AppModel.php thus enabling Autocache for all models 61 | 62 | public $actsAs = array('Autocache.Autocache'); 63 | 64 | Note the Behavior settings possible here in the section below. 65 | 66 | ### Step 5 67 | Add an 'autocache' parameter to your find query, see further below for the various 68 | options you have here but it can be as simple as just 'autocache' => true. 69 | 70 | ### Step 6 71 | Fire up the AutocachePlugin Tests, they should all pass. 72 | 73 | 74 | Usage 75 | ----- 76 | 77 | ### Find Parameter Options 78 | 79 | This is the fun stuff, and where Autocache really shines the with its simplicity 80 | in usage, there are just three options:- 81 | 82 | - config (string) = the cache name specified by using a standard Cake 83 | Cache::config('a_cache_name', ... ) 84 | 85 | - name (string) = the developer can override an automatically generated cache 86 | key name by specifying it as an option, doing this will give you a small 87 | performance gain because we don't have to a serialization and md5 to generate 88 | a cache key name - see Q&A below for note on cache key name generation. 89 | 90 | - flush (bool) = you can force a reload from the datasource by adding a flush 91 | option, which will delete any existing cached value and replace it with a 92 | fresh datastore find result. 93 | 94 | Because the 'config' option is the most commonly required option we make it easy 95 | to access it in the following ways. 96 | 97 | - if the 'autocache' option is bool, we use the cache configuration specified when 98 | the Autocache Behavior was assigned to the Model, ie the "default_cache" parameter 99 | 100 | - if the 'autocache' option is a string, we use this string as the cache 101 | configuration name to use. 102 | 103 | ### Find Parameter Option Examples 104 | 105 | $params = array('autocache'=>true) 106 | 107 | $params = array('autocache'=>'default') 108 | 109 | $params = array('autocache'=>array('config'=>'default')) 110 | 111 | $params = array('autocache'=>array('name'=>'some_name')) 112 | 113 | $params = array('autocache'=>array('flush'=>true)) 114 | 115 | The first three are essentially the same thing expressed differently 116 | 117 | ### The Model->autocache_is_from variable 118 | I've erred about the wisdom of this, non-the-less it's there. After a result is 119 | handed to you from a Model find(), you can check Model->autocache_is_from 120 | to determine if the result was from cache or not. Beware, if you want a portable 121 | Model code you'll need to do an isset() to test for variable existence because if 122 | the behavior is not there the variable will not be set... 123 | 124 | ### Autocache Behavior Settings 125 | 126 | - cache_config_name_default << defines the default cache configuration name to 127 | use when a find query contains an 'autocache' parameter without an explicit 128 | cache configuration name. By default the name is 'default' 129 | 130 | - cache_config_name_check << tells Autocache to check if the cache configuration 131 | name that is about to be used has actually been defined, this helps you prevent 132 | silly mistakes. By default this parameter sets itself to true when 133 | Configure::read('debug') is greater than 0 and otherwise false. There may be 134 | a small speed improvement in setting this to false. 135 | 136 | - dummy_datasource << defines the dummy datastore name that needs to be 137 | defined in your database.php file. By default it's named 'autocache' and it's 138 | pretty unlikely you'd ever need to change this. 139 | 140 | ### Autocache Behavior Setting Examples 141 | 142 | From the model:- 143 | 144 | $actsAs = array('Autocache.Autocache') 145 | 146 | $actsAs = array('Autocache.Autocache',array('default_cache'=>'level_1')); 147 | 148 | $actsAs = array('Autocache.Autocache',array('default_cache'=>'level_1','check_cache'=>false)); 149 | 150 | From the Controller through a Behavior "attach":- 151 | 152 | $this->MyModelName->Behaviors->attach('Autocache.Autocache',array('default_cache'=>'level_2')); 153 | 154 | 155 | Questions and Answers 156 | --------------------- 157 | 158 | Q: What about cache grouping? 159 | A: Grouping based on (Model + Cache Config) is a great idea, unfortunately as at 160 | CakePHP 2.5.7 it does not *seem* possible since after the initial call to 161 | Cache::config() it is not possible to define new groups. The work-around for 162 | this is to implement per-group configs, however, if you do this calls to 163 | Cache::clear() appear to cause subsequent calls to Cache::write() to not write 164 | to the (FileEngine) cache - I'd really like to be shown wrong on this, more time 165 | required to nut out a solution, cache grouping is a good thing. 166 | 167 | Q: I want more control over cache times, cache locations etc. 168 | A: It's right in front of you :) The way to achieve this is to specify a new 169 | cache configuration for the "stuff" you are wanting to cache. This allows you 170 | to get really funky, create a config that caches in APC while others cache to 171 | File and establish different time frames for each, for example:- 172 | 173 | Cache::config('default', array('engine' => 'APC', 'duration'=>'60 seconds')); 174 | Cache::config('level_05', array('engine' => 'APC', 'duration'=>'5 second')); 175 | Cache::config('level_1', array('engine' => 'APC', 'duration'=>'1 minute')); 176 | Cache::config('level_2', array('engine' => 'APC', 'duration'=>'5 minute')); 177 | Cache::config('level_3', array('engine' => 'APC', 'duration'=>'30 minute')); 178 | Cache::config('level_4', array('engine' => 'File','duration'=>'4 hour')); 179 | Cache::config('level_5', array('engine' => 'File','duration'=>'1 day')); 180 | 181 | Q: How does Autocache name cached data? 182 | A: Take a look at _generateCacheName() the crux of the matter is that we take 183 | the all query parameters, serialize them and take a hash of the result 184 | thus ensuring a useful unique name per query - yes, there is overhead in 185 | doing this but it's still less than doing a database query! 186 | 187 | Q: What's AutocacheSource (ie the DummySource) all about? 188 | A: In order to prevent the CakePHP Model class from making a full request to 189 | the database when we have a cached result we need a way to quickly cause 190 | the find() query to return with nothing so we can re-inject the result 191 | in the afterFind() callback - it's unfortunate this behavior requires more 192 | than one .php file, but that's the way it is - still much tidier than the 193 | previous approach that involved cutting'n'pasting code into the AppModel. 194 | 195 | Q: What's the history? 196 | A: AutocachePlugin follows on from AutocacheBehavior which was an an improvement 197 | on "Automatic model data caching for CakePHP" that I wrote a while back. I 198 | borrowed from ideas "jamienay" had put forward in his automatic_query_caching 199 | 200 | Q: What's different about CakephpAutocachePlugin to CakephpAutocacheBehavior? 201 | A: Probably the biggest "thing" is the change in the find parameter option name 202 | from 'cache' to 'autocache' - yes the option name is longer but it is clearer 203 | and better aligns with the rest of naming. Other stuff includes, there is no 204 | need to specify the autocache datasource configuration anymore, we deal with 205 | that automatically while it can be overridden via the dummy_datasource parameter. 206 | ... the the Plugin is just easier to use! 207 | -------------------------------------------------------------------------------- /Test/Case/Model/Behavior/AutocachePluginTest.php: -------------------------------------------------------------------------------- 1 | cache_path = CACHE; 48 | 49 | Cache::config('default', array( 50 | //'prefix' => 'ac_', 51 | 'engine' => 'File', 52 | 'path' => $this->cache_path, 53 | 'duration' => '+10 seconds' 54 | )); 55 | 56 | $this->Article = ClassRegistry::init('Article'); 57 | $this->User = ClassRegistry::init('User'); 58 | } 59 | 60 | /** 61 | * endTest 62 | * 63 | * @return void 64 | */ 65 | public function endTest($method) { 66 | unset($this->Article); 67 | } 68 | 69 | /** 70 | * testGetCachedTrueFirst 71 | * 72 | * @return void 73 | */ 74 | public function testGetCachedTrueFirst() { 75 | $autocache = true; 76 | $this->_cacheTest($autocache, __FUNCTION__); 77 | } 78 | 79 | /** 80 | * testGetCachedTrueSecond 81 | * 82 | * @return void 83 | */ 84 | public function testGetCachedTrueSecond() { 85 | $autocache = true; 86 | $this->_cacheTest($autocache, __FUNCTION__); 87 | } 88 | 89 | /** 90 | * testGetCachedNamedConfigString 91 | * 92 | * @return void 93 | */ 94 | public function testGetCachedNamedConfigString() { 95 | $autocache = 'default'; 96 | $this->_cacheTest($autocache, __FUNCTION__); 97 | } 98 | 99 | /** 100 | * testGetCachedNamedConfig 101 | * 102 | * @return void 103 | */ 104 | public function testGetCachedNamedConfig() { 105 | $autocache = array('config' => 'default'); 106 | $this->_cacheTest($autocache, __FUNCTION__); 107 | } 108 | 109 | /** 110 | * testGetCachedNamedConfigName 111 | * 112 | * @return void 113 | */ 114 | public function testGetCachedNamedConfigName() { 115 | $autocache = array('name' => 'a_name_for_a_cache_1'); 116 | $this->_cacheTest($autocache, __FUNCTION__); 117 | } 118 | 119 | /** 120 | * testGetCachedNamedConfigNameAndConfig 121 | * 122 | * @return void 123 | */ 124 | public function testGetCachedNamedConfigNameAndConfig() { 125 | $autocache = array('config' => 'default', 'name' => 'a_name_for_a_cache_2'); 126 | $this->_cacheTest($autocache, __FUNCTION__); 127 | } 128 | 129 | /** 130 | * testFlushedCache 131 | * 132 | * @return void 133 | */ 134 | public function testFlushedCache() { 135 | $autocache = array('flush' => true); 136 | $this->_cacheTest($autocache, __FUNCTION__); 137 | } 138 | 139 | /** 140 | * testFlushedCache 141 | * 142 | * @return void 143 | */ 144 | public function testFlushedWithConfig() { 145 | $autocache = array('config' => 'default', 'flush' => true); 146 | $this->_cacheTest($autocache, __FUNCTION__); 147 | } 148 | 149 | /** 150 | * testGetCachedWithContainable 151 | * - Article JOIN User 152 | * 153 | * @return void 154 | */ 155 | public function testGetCachedWithContainable() { 156 | 157 | Cache::clear(); 158 | 159 | $this->User->Behaviors->attach('Containable'); 160 | 161 | $conditions = array( 162 | 'contain' => array('Article'), 163 | 'autocache' => true 164 | ); 165 | 166 | $result_1 = $this->User->find('first', $conditions); 167 | 168 | $this->assertTrue(!empty($result_1)); 169 | $this->assertFalse($this->User->autocache_is_from); 170 | 171 | # check if filename starting with "cake_autocache_first_article_" exists 172 | $files = $this->_glob_recursive($this->cache_path . 'cake_autocache_first_user_*'); 173 | $this->assertTrue((1 === count($files))); // always 1 because Cache::clear(); is used above 174 | # Second query result should equal first query 175 | $result_2 = $this->User->find('first', $conditions); 176 | $this->assertTrue(!empty($result_2)); 177 | 178 | $this->assertTrue($this->User->autocache_is_from); 179 | 180 | // Check the first query result is the same as the second 181 | $this->assertSame($result_1, $result_2); 182 | } 183 | 184 | /** 185 | * testDatasourceFunctions 186 | * 187 | * @return void 188 | */ 189 | public function testDatasourceFunctions() { 190 | 191 | Cache::clear(); 192 | 193 | $this->User->Behaviors->attach('Containable'); 194 | 195 | $conditions = array( 196 | 'contain' => array('Article'), 197 | 'autocache' => true 198 | ); 199 | 200 | $result_1 = $this->User->find('first', $conditions); 201 | $this->assertTrue(!empty($result_1)); 202 | 203 | $db = $this->User->getDataSource(); 204 | $this->assertTrue(!empty($db->name('count'))); 205 | $this->assertTrue(is_object($db->identifier('foobar'))); 206 | } 207 | 208 | /** 209 | * testCallbacks 210 | * 211 | * @return void 212 | */ 213 | public function testCallbacks() { 214 | 215 | Cache::clear(); 216 | 217 | $autocache = array('config' => 'default'); 218 | $user_id = 3; 219 | $conditions = array('id'=>$user_id); 220 | 221 | $result_3 = $this->User->find('all',array( 222 | 'conditions' => $conditions, 223 | 'autocache' => $autocache 224 | )); 225 | debug($result_3); 226 | $this->assertFalse($this->User->autocache_is_from); 227 | 228 | $this->User->id = $user_id; 229 | $username = md5(time(true)); 230 | $this->User->saveField('user', $username); 231 | 232 | $result_4 = $this->User->find('all',array( 233 | 'conditions' => $conditions, 234 | 'autocache' => $autocache 235 | )); 236 | debug($result_4); 237 | $this->assertFalse($this->User->autocache_is_from); 238 | 239 | $result_5 = $this->User->find('all',array( 240 | 'conditions' => $conditions, 241 | 'autocache' => $autocache 242 | )); 243 | debug($result_5); 244 | $this->assertTrue($this->User->autocache_is_from); 245 | 246 | } 247 | 248 | /** 249 | * _cacheTest 250 | * 251 | * @param mixed $autocache 252 | * @return void 253 | */ 254 | protected function _cacheTest($autocache, $test_name='unknown') { 255 | 256 | Cache::clear(); 257 | 258 | // First query gets cached 259 | $result_1 = $this->Article->find('first', array('autocache' => $autocache)); 260 | 261 | $this->assertTrue(!empty($result_1)); 262 | $this->assertFalse($this->Article->autocache_is_from); 263 | 264 | # check if filename starting with "cake_autocache_first_article_" exists 265 | $files = $this->_glob_recursive($this->cache_path . 'cake_autocache*'); 266 | if (is_array($autocache) && isset($autocache['name'])) { 267 | $files = $this->_glob_recursive($this->cache_path . 'cake_' . $autocache['name'] . '*'); 268 | } 269 | 270 | if (isset($autocache['flush']) && $autocache['flush'] !== true) { 271 | $this->assertTrue((1 === count($files))); // always 1 because Cache::clear(); is used above 272 | } 273 | 274 | // Count number of queries before second query for the same data 275 | $query_count_1 = $this->_queryCount(); 276 | 277 | # Second query result should equal first query 278 | $result_2 = $this->Article->find('first', array('autocache' => $autocache)); 279 | $this->assertTrue(!empty($result_2)); 280 | 281 | // Count number of queries before second query for the same data 282 | $query_count_2 = $this->_queryCount(); 283 | 284 | // If flushed we do expect the query to be run so we must compensate 285 | if (isset($autocache['flush']) && true === $autocache['flush'] ) { 286 | $this->assertFalse($this->Article->autocache_is_from); 287 | $this->assertSame($query_count_1, $query_count_2 - 1); 288 | } else { 289 | // Check we still have the same query count and thus did not touch the database 290 | $this->assertTrue($this->Article->autocache_is_from); 291 | $this->assertSame($query_count_1, $query_count_2 ); 292 | } 293 | 294 | // Check the first query result is the same as the second 295 | $this->assertSame($result_1, $result_2); 296 | 297 | } 298 | 299 | /** 300 | * return all queries 301 | * 302 | * @return array 303 | * @access protected 304 | */ 305 | protected function _queries() { 306 | $res = $this->db->getLog(false, false); 307 | $queries = $res['log']; 308 | $return = array(); 309 | foreach ($queries as $row) { 310 | if (strpos($row['query'], 'DESCRIBE') === 0 || strpos($row['query'], 'BEGIN') === 0 || strpos($row['query'], 'COMMIT') === 0) { 311 | continue; 312 | } 313 | $return[] = $row['query']; 314 | } 315 | debug($return); 316 | return $return; 317 | } 318 | 319 | /** 320 | * return number of queries executed 321 | * 322 | * @return int 323 | * @access protected 324 | */ 325 | protected function _queryCount() { 326 | return count($this->_queries()); 327 | } 328 | 329 | /** 330 | * _glob_recursive 331 | * @param type $pattern 332 | * @param type $flags 333 | * @return type 334 | */ 335 | protected function _glob_recursive($pattern, $flags = 0) { 336 | $files = glob($pattern, $flags); 337 | 338 | foreach (glob(dirname($pattern) . '/*', GLOB_ONLYDIR | GLOB_NOSORT) as $dir) { 339 | $files = array_merge($files, $this->_glob_recursive($dir . '/' . basename($pattern), $flags)); 340 | } 341 | return $files; 342 | } 343 | 344 | } 345 | 346 | /** 347 | * User model 348 | * 349 | */ 350 | class User extends CakeTestModel { 351 | 352 | /** 353 | * Behaviors 354 | * 355 | * @var array 356 | */ 357 | public $actsAs = array('Autocache.Autocache'); 358 | 359 | /** 360 | * hasMany associations 361 | * 362 | * @var array 363 | */ 364 | public $hasMany = array( 365 | 'Article' => array( 366 | 'className' => 'Article', 367 | 'foreignKey' => 'user_id', 368 | ) 369 | ); 370 | 371 | } 372 | 373 | /** 374 | * Article model 375 | * 376 | */ 377 | class Article extends CakeTestModel { 378 | 379 | /** 380 | * Behaviors 381 | * 382 | * @var array 383 | */ 384 | public $actsAs = array('Autocache.Autocache'); 385 | 386 | } 387 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "verbnetworks/cakephp-autocache-plugin", 3 | "description": "CakephpAutocachePlugin is a CakePHP 2.2+ Plugin that makes query caching as easy as adding a 'autocache'=>true parameter to your Model query", 4 | "type": "cakephp-plugin", 5 | "keywords": [ "cakephp", "plugin", "cache" ], 6 | "homepage": "https://github.com/verbnetworks/CakephpAutocachePlugin", 7 | "authors": [{ 8 | "name": "Nicholas de Jong", "email": "contact@verbnetworks.com" 9 | }], 10 | "require": { "composer/installers": "*" }, 11 | "extra": { "installer-name": "Autocache" } 12 | } 13 | --------------------------------------------------------------------------------