Skip to content

Instantly share code, notes, and snippets.

@kemo
Created April 27, 2012 21:36
Show Gist options
  • Save kemo/2513471 to your computer and use it in GitHub Desktop.
Save kemo/2513471 to your computer and use it in GitHub Desktop.
Cache-powered ORM model extension (Kohana 3.3) - [!!] Not tested
<?php defined('SYSPATH') or die('No direct script access.');
/**
* Extend this class if you want your model cached. Example usage:
*
* 1. Model
* class Model_User extends ORM_Cached {}
*
* 2. Usage
*
* // This will either retrieve the user with specified ID from cache
* // or get him from the database and cache him anyways
* $user = Model_User::cache($this->request->param('id'));
*
* // This will get the user with username "Jason" and email "[email protected]"
* // and also save him by these params (not the PK)
* $user = Model_User::cache(array('username' => 'Jason','email' => '[email protected]'));
*
* // Now if we update this user, related cache will also be updated
* $user->set('password' => 'test')->update();
*
* // And if we delete this user, related cache will also be deleted
* $user->delete();
*
* @package ORM
* @author Kemal Delalic <[email protected]>
*/
class ORM_Cached extends ORM {
/**
* @var string Default cache group, e.g. 'file' or 'memcached'
*/
protected static $_cacher_driver = 'apc';
/**
* @var int For how long should the cache for current model last?
*/
protected static $_cacher_lifetime = 60;
/**
* List of columns to be serialized when going to 'sleep'
*
* @var array
* @see http://php.net/serializable
*/
protected static $_serialized_columns = array(
'_cache_loader_key',
'_primary_key_value',
'_object',
'_changed',
'_loaded',
'_saved',
'_sorting',
'_original_values',
);
/**
* Cached "factory" method
*
* $post = Model_User::cache($request->param('id'));
*
* @param mixed $id primary key or array of values to be matched
* @return ORM_Cached
*/
public static function cache($id)
{
$model = get_called_class();
$cache_key = static::cache_key($model, $id);
if ( ! $object = static::cacher()->get($cache_key))
{
$object = new $model($id);
// Only save the object cache if its' loaded
if ($object->loaded())
{
// Save the loader key into the object (to use when deleting the cache)
$object->cache_loader_key($cache_key);
// Save this objects' cache
static::save_cache($object);
}
}
return $object;
}
/**
* Retrieves the cache driver for current model
*
* @return Cache
*/
public static function cacher()
{
return Cache::instance(static::$_cacher_driver);
}
/**
* Returns the cache key for ORM_Cached::cache()
*
* @param string $model
* @param mixed $id
* @return string
*/
public static function cache_key($model, $id)
{
if (is_array($id))
{
// Sort the passed array keys for consistency
ksort($id);
$id = http_build_query($id, '', '::');
}
elseif (Valid::digit($id))
{
// Make sure the key is a string to have consistent cache keys
$id = (string) $id;
}
elseif ( ! is_string($id))
{
throw new Kohana_Exception('Unsupported $id type: :type',
array(':type' => gettype($id)));
}
return $model.':::'.$id;
}
/**
* Deletes the cache for passed ORM_Cached object
*
* @param ORM_Cached $object
* @return void
*/
public static function delete_cache(ORM_Cached $object)
{
if ($key = $object->cache_loader_key())
{
static::cacher()->delete($key);
}
}
/**
* Saves the cache for passed ORM object
*
* @param ORM $object
* @return void
*/
public static function save_cache(ORM_Cached $object)
{
if ($key = $object->cache_loader_key())
{
static::cacher()->set($key, $object, static::$_cacher_lifetime);
}
}
/**
* @var string The key current object was loaded with, if any
*/
protected $_cache_loader_key;
/**
* @var boolean Should table columns be reloaded on wakeup?
*/
protected $_reload_on_wakeup = FALSE;
/**
* ORM contructor overloaded to auto-assign a cache key if none provided
*
* @param mixed $id
*/
public function __construct($id = NULL)
{
parent::__construct($id);
// If the current record has been loaded and no cache key is set,
// use the primary key for creating a cache key
if ($this->_loaded AND ! $this->cache_loader_key())
{
$this->cache_loader_key(static::cache_key(get_class($this), $this->pk()));
}
}
/**
* Cache loader key setter / getter
*
* @param mixed $key to set
* @return string|self
* @chainable (on set)
*/
public function cache_loader_key($key = NULL)
{
if ($key === NULL)
return $this->_cache_loader_key;
$this->_cache_loader_key = $key;
return $this;
}
/**
* Overloaded create method
* Once the object is created, cache will be saved
*
* @param Validation $validation
* @return $this (chainable)
*/
public function create(Validation $validation = NULL)
{
$result = parent::create($validation);
// Assign the cache loader key now that we have the primary key
$this->cache_loader_key(static::cache_key(get_called_class(), $this->pk()));
// Save the cache for this object
static::save_cache($this);
return $result;
}
/**
* Delete override with cache support
*
* @chainable
* @return $this
*/
public function delete()
{
static::delete_cache($this);
return parent::delete();
}
/**
* Proxy method to Database list_columns.
*
* @cached
* @return array
*/
public function list_columns()
{
$cache_key = "{$this->_table_name}::list_columns()";
if ( ! $columns = ORM_Cached::cacher()->get($cache_key))
{
$columns = $this->_db->list_columns($this->_table_name);
ORM_Cached::cacher()->set($cache_key, $columns, Date::MINUTE);
}
return $columns;
}
/**
* Allows serialization of only the object data and state, to prevent
* "stale" objects being unserialized, which also requires less memory.
*
* @return array
*/
public function serialize()
{
$data = array();
// Store only information about the object
foreach (static::$_serialized_columns as $var)
{
$data[$var] = $this->{$var};
}
return serialize($data);
}
/**
* Update override with cache support
*
* @param Validation $validation
* @return $this
* @throws ORM_Validation_Exception
*/
public function update(Validation $validation = NULL)
{
$result = parent::update($validation);
static::save_cache($this);
return $result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment