<?php /** * * Models for redis! * * @author Chema <chema@garridodiaz.com> * @package Core * @version 0.1 * @license GPL v3 * * @todo * On update only save fields that are modified. * Fields validation, integer, varchar, email etc... * Create relations hasone , hasmany... * Fields filters, on read, on save. * if value is array: use a list 'TABLENAME:PK:SETNAME' and save it in a common list or set */
class Remodel {
/** * fields with values * @var array */ protected $_data = array();
/** * fields that has this model, if empty everything is allowed, recommended to set it. * @var array */ protected $_fields = array();
/** * redis instance * @var Predis */ protected $_redis;
/** * common name used as redis key * @var string */ protected $_table_name;
/** * @var string PrimaryKey field name */ protected $_primary_key;
/** * redis keys constants to concat * DONOT change this if you had data already in the REDIS */ const NEXT_PK = '_pk_'; // 'TABLENAME:_pk_' => stores the pk counter const ALL = '_all_'; // 'TABLENAME:ALL' => stores all the ids for this table const KSEP = ':'; // Key Separator used for any KEY /** * create object and connect * @param Predis\Client $redis connection or pipe * @param array $redis_config configuration * @param integer $pk PK id to load. */ public function __construct($redis = NULL, array $redis_config = NULL , $pk = NULL ) { if($redis!==NULL) { $this->_redis = $redis; } else { try { $this->_redis = new Predis\Client($redis_config); } catch (Exception $e) { throw new Exception("Error connecting to redis: ".print_r($e,1), 1); } }
if (is_numeric($pk)) $this->load($pk); }
/** * Create a new model instance. * * $model = Remodel::factory($name); * * @param string model name * @return Model */ public static function factory($name) { $class = 'Model_'.$name; if(class_exists($class)) { return new $class; } else { throw new Exception('Factory model not found: '.$name, 1); } }
/** * creates a new item pk * @param Redis $pipe usage of pipe to make it faster optional * @return boolean */ public function create($pipe = NULL) { $conn = ($pipe!==NULL)? $pipe:$this->_redis;
//getting the primary key, first we increase so there¡s no repeated id's $this->pk($this->get_last_pk(TRUE)); //save the data at redis if ($this->update($conn)) { //save the primary in a global set $conn->sadd($this->_table_name.self::KSEP .self::ALL, $this->pk()); //end pipe return TRUE; } //if save not success decrease else { $this->_redis->decr($this->_table_name.self::KSEP.self::NEXT_PK); return FALSE; } }
/** * update the current data at redis * @param Redis $pipe usage of pipe to make it faster optional * @return boolean */ public function update($pipe = NULL) { $conn = ($pipe!==NULL)? $pipe:$this->_redis;
//we add the pk to the array $data = array_merge(array( $this->_primary_key => $this->pk() ),$this->_data);
return $conn->hmset($this->_table_name.self::KSEP.$this->pk(), $data); } /** * deletes a key from the redis * @param integer $pk optional * @return boolean */ public function delete($pk = NULL) { //if isnumeric we try to be friendly and use it as the index if (!is_numeric($pk)) $pk = $this->pk(); //remove the KEY from redis $ret = $this->_redis->del($this->_table_name.self::KSEP.$pk);
//delete the PK from the global list if ($ret>0) $ret = $this->_redis->srem($this->_table_name.self::KSEP.self::ALL, $this->pk()); $this->unload();
return ($ret>0)? TRUE : FALSE; } /** * updates or creates, shortcut * @param Redis $pipe usage of pipe to make it faster optional */ public function save($pipe = NULL) { //if index exists calls update if doesn't exists create ($this->loaded())? $this->update($pipe):$this->create($pipe); } /** * Just tells you if there's some data populated in the model using the primary * @return boolean */ public function loaded() { return (array_key_exists($this->_primary_key, $this->_data)); } /** * Unloads any data */ public function unload() { unset($this->_data); }
/** * loads values * @param inteeger PK to load data * @return boolean */ public function load($pk = NULL) { if (is_numeric($pk)) { $data = $this->_redis->hgetall($this->_table_name.self::KSEP.$pk); if (!empty($data)) { $this->values($data); return TRUE; } } return FALSE; }
/** * loads/get an array of values into the model * @param array $data * @return boolean/array */ public function values(array $data = NULL) { if (is_array($data)) { $this->_data = $data; return TRUE; } else return $this->_data; }
/** * retrieves models for the specified range * lrange over TABLENAME:ALL * @param integer/array $elements if array return those elements, if not = from * @param integer $stop to * @return array remodel */ public function select($elements = 0, $stop = 9) { if (!is_array($elements)) $elements = $this->get_all_pk($elements,$stop);
$ret = array(); // return array of Models if(count($elements) >= 1) { //@todo use of a pipe to improve load? //get values in a pipe put them in array and push them to the model $class = get_class($this);//using same model foreach ($elements as $element) { $model = new $class; $model->load($element); if ($model->loaded()) $ret[]= $model; unset($model); } }
return $ret; }
/** * returns the amount of elements in the redis set for that table * @param string set_name * @return integer */ public function count($set_name = NULL) { //count all if ($set_name === NULL) return $this->_redis->scard($this->_table_name.self::KSEP.self::ALL); else//count set return $this->_redis->scard($this->_table_name.self::KSEP.$set_name); } /** * set/get the primary key value of the object * @return integer id */ public function pk($pk = NULL) { //acting as setter if (is_numeric($pk)) $this->_data[$this->_primary_key] = $pk;
return ($this->loaded())? $this->_data[$this->_primary_key] : FALSE; }
/** * retrieve the PK for the list of this table * @param integer $start from * @param integer $stop to * @return array */ public function get_all_pk($start = 0, $stop = 9) { //todo improve this!! return $this->_redis->sort($this->_table_name.self::KSEP.self::ALL,array('by'=> 'nosort','limit' => array($start,$stop))); }
/** * retrieve the last PK used for the table * @param bool $increase if set to true we increase the redis PK before. * @return integer */ public function get_last_pk($increase = FALSE) { if ($increase === TRUE) $this->_redis->incr($this->_table_name.self::KSEP.self::NEXT_PK);
return $this->_redis->get($this->_table_name.self::KSEP.self::NEXT_PK); } /** * Magic methods to set get */ public function __set($name, $value) { //check if fields exists in the `model` if ( empty($this->_fields) OR in_array($name, $this->_fields) ) { $this->_data[$name] = $value; } else throw new Exception($name.' does not exist in the model.', 1); }
public function __get($name) { return (array_key_exists($name, $this->_data)) ? $this->_data[$name] : NULL; }
public function __isset($name) { return isset($this->_data[$name]); }
public function __unset($name) { unset($this->_data[$name]); } }
|