<?php
/**
 * This behaviour add the possibility to cache the result of SQL queries.
 *
 * To use the cache, call cache_find() instead of find()
 *
 * The second parameter array can contain a 'cache' key with the following options:
 *
 *  	'cache' => 'key name'												//the key used to store the result in cache
 *  	'cache' => array('keyname', '1 hour') 								//the key + the duration
 *  	'cache' => array('key' => 'keyname', 'duration' => '1 hour') 		//the key + the duration
 *
 *  If the key 'cache' is not set in your find parameters:
 *
 *  								- it uses the default Behaviour value set for duration
 *  								- the key is generated by using a hash of the find parameters
 *
 *
 *	Some ideas used in this behaviour come from a post of Miles Johnson that can be found here: http://www.milesj.me/blog/read/34/cacheing-each-query-individually
 *
 *
 * @author   Nicolas Rod <nico@alaxos.com>
 * @license  http://www.opensource.org/licenses/mit-license.php The MIT License
 * @link     http://www.alaxos.ch
 */
class CacheBehavior extends ModelBehavior
{
    function setup(&$model, $settings)
    {
        $this->settings[$model->alias]['default_duration'] = isset($settings['default_duration']) ? $settings['default_duration'] : '1 hour';
        $this->settings[$model->alias]['engine']           = isset($settings['engine'])           ? $settings['engine']           : 'File';
        $this->settings[$model->alias]['path']             = isset($settings['path'])             ? $settings['path']             : CACHE .'sql'. DS;
        
        Cache :: config('cache_behaviour', array(
                                        'engine'	=> $this->settings[$model->alias]['engine'],
                                        'path'		=> $this->settings[$model->alias]['path'],
                                        'prefix' 	=> strtolower($model->name) . '-',
                                        'serialize'	=> true,
                	                ));
    }
    
    function cache_find(&$model, $conditions = null, $fields = array(), $order = null, $recursive = null)
    {
        if (Configure::read('Cache.disable') !== true && Configure::read('Cache.check') === true)
    	{
    	    if(isset($fields['cache']))
    	    {
    	        if(is_array($fields['cache']))
        	    {
        	        if(isset($fields['cache']['key']))
        	        {
        	            /*
        	             * array('key' => 'mykey', 'duration' => '1 hour')
        	             */
        	            
            	        $key      = $fields['cache']['key'];
            	        $duration = isset($fields['cache']['duration']) ? $fields['cache']['duration'] : $this->settings[$model->alias]['default_duration'];
        	        }
        	        elseif(isset($fields['cache']['duration']))
        	        {
        	            /*
        	             * array('key' => 'mykey', 'duration' => '1 hour')
        	             */
        	            
            	        $key      = $this->generate_key($conditions, $fields, $order, $recursive);
            	        $duration = $fields['cache']['duration'];
        	        }
        	        else
        	        {
        	            /*
        	             * array('mykey', '1 hour')
        	             */
        	            
        	            $key      = $fields['cache'][0];
        	            $duration = count($fields['cache']) == 2 ? $fields['cache'][1] : $this->settings[$model->alias]['default_duration'];
        	        }
        	    }
        	    else
        	    {
        	    	if(is_string($fields['cache']))
        	    	{
        	        	$key = $fields['cache'];
        	    	}
        	    	else
        	    	{
        	    		$key = $this->generate_key($conditions, $fields, $order, $recursive);
        	    	}
        	    	
        	        $duration = $this->settings[$model->alias]['default_duration'];
        	    }
    	    }
    	    else
    	    {
    	        $key          = $this->generate_key($conditions, $fields, $order, $recursive);
    	        $duration     = $this->settings[$model->alias]['default_duration'];
    	    }
    	    
    		// Set cache settings
    		Cache :: config('cache_behaviour', array('duration'	=> $duration));
    		
    		// Load from cache
    		$results = Cache :: read($key, 'cache_behaviour');
    		
    		if (!is_array($results))
    		{
    			$results = $model->find($conditions, $fields, $order, $recursive);
    			Cache :: write($key, $results, 'cache_behaviour');
    		}
    		else
    		{
    		    /*
        	     * Reset the model associations
        	     *
        	     * Note:
        	     * 		as the model->find() method is not called -> resetAssociations() is not done yet
        	     * 		-> to be consistent, we call it manually here
        	     */
        	    $model->resetAssociations();
    		}
    		
    		return $results;
    	}
    	else
    	{
        	/*
        	 * caching is disabled in core.php
        	 */
    	    
        	return $model->find($conditions, $fields, $order, $recursive);
    	}
    }
    
    /**
     * Clear a cached query identified by the given key
     *
     * @param $cache_key string
     * @return boolean
     */
    function clear_cached_find(&$model, $key)
    {
        return Cache :: delete($key, 'cache_behaviour');
    }
    
    /**
     * Generate a key depending on the find parameters given
     *
     * @param $conditions
     * @param $fields
     * @param $order
     * @param $recursive
     * @return string
     */
    function generate_key($conditions = null, $fields = array(), $order = null, $recursive = null)
    {
        $conditions_s = isset($conditions) ? Security :: hash(serialize($conditions)) : null;
        $fields_s     = isset($fields)     ? Security :: hash(serialize($fields))     : null;
        $order_s      = isset($order)      ? Security :: hash(serialize($order))      : null;
        $recursive_s  = isset($recursive)  ? Security :: hash(serialize($recursive))  : null;
        
        $key          = $conditions_s . '_' . $fields_s . '_' . $order_s . '_' . $recursive_s;
        
        return $key;
    }
}
?>