dba = $dba; $this->model = $model; if(!empty($data)) { $this->saved = TRUE; } $this->data = $data; } /** * Get the value of a row from the record. First it will look in the dirty array, then the data (saved) * array, then the cache from functions, then it will check if a $key 'func_name' function exists as * 'getFuncName', and if so, cache that result. * @param string $key The table column name or cache result to get. * @return mixed * @see VK_Overload * @see Record::__set(), Record::__isset(), Record::__unset() */ final public function __get($key = '') { $ret = NULL; // dirty first, important if(isset($this->dirty[$key])) { $ret = $this->dirty[$key]; } else if(isset($this->data[$key])) { $ret = $this->data[$key]; } else if(isset($this->cache[$key])) { $ret = $this->cache[$key]; } else { $method_name = 'get'. camelize($key); if(method_exists($this, $method_name)) { $this->cache[$key] = $this->$method_name(); $ret = $this->cache[$key]; } } return $ret; } /** * Set a value to the array of dirty data. * @param string $key The key (column name) to set the the dirty array * @param string $val The value to set the the dirty array for this column * @see VK_Overload * @see Record::__get(), Record::__isset(), Record::__unset() */ final public function __set($key = '', $val) { $this->dirty[$key] = $val; } /** * Check if a value for a column name exists * @param string $key The key (column name) check * @see VK_Overload * @see Record::__get(), Record::__set(), Record::__unset() */ final public function __isset($key = '') { return isset($this->dirty[$key]) || isset($this->data[$key]) || isset($this->cache[$key]); } /** * Unset the value for a column name * @param string $key The key (column name) to unset * @see VK_Overload * @see Record::__get(), Record::__set(), Record::__isset() */ final public function __unset($key = '') { unset($this->dirty[$key], $this->data[$key]); } /** * Overrideable function (in custom record classes) to execute before a record is * saved. This is particularly useful if you want to do any sort of operations on the * values of columns before saving, eg: special validation/cleaning. */ public function preSave() { } /** * Overrideable function (in custom record classes) to execute after a record has * been saved. I haven't thought of a use case for this, but because preSave exists, * I thought: "why not postSave?", and so it is here. */ public function postSave() { } /** * Save a record to the database. If the record already exists in the database, then this * will perform an UPDATE query, otherwise it will perform an INSERT query. */ final public function save() { $this->preSave(); $insert_vals = array(); $keys = array_keys($this->dirty); $vals = array_values($this->dirty); // validate the dirty things against the model $this->model->validate($keys, $vals); // update if($this->saved) { $sql = 'UPDATE '. $this->model->table['name'] .' SET '; $sep = ''; foreach($keys as $key) { $sql .= $sep . $key .'=?'; $sep = ','; } $this->dba->update($sql, $vals); // insert, same as Finder::insertOne } else { $this->dba->update($this->dba->buildInsert($this->model->table['name'], $keys, $vals), $vals); } if($this->model->primary !== NULL) { $this->data[$this->model->primary] = $this->dba->insertId(); } $this->saved = TRUE; $this->data = array_merge($this->data, $this->dirty); $this->dirty = array(); $this->postSave(); return TRUE; } /** * Delete a record from the database if it exists there already. */ final public function delete() { if($this->saved) { if($this->model->primary !== NULL) { $this->dba->update('DELETE FROM '. $this->model->table['name'] .' WHERE '. $this->model->primary .'=?', array($this->data[$this->model->primary])); } else { // hrm. } } } } /** * A class it iterate over a result set. Each iteration will return a Record * decorator class for the row. This class extends IteratorIterator because the * database result sets passed in implement Iterator. * @see Record, IteratorIterator, DatabaseResultSet */ class RecordSet extends IteratorIterator { /** * A reference to the current database abstraction layer * @see Database * @internal */ private $dba; /** * A reference to this tables model definition * @see ModelDefinition * @internal */ private $model; /** * An array of cached record classes. The record classes are cached because * if one decides to iterate over this result set multiple times then each * time we want to return to them the same record class for each database row. * @internal */ private $cache = array(); /** * Constructor, bring in the result set, the database layer and the model definition. * @param mixed $result A reference to a DatabaseResultSet * @param mixed $dba A reference to the current database abstraction layer * @param mixed $model A reference to the this tables model definition * @see DatabaseResultSet, Database, ModelDefinition * @internal */ final public function __construct(DatabaseResultSet $result, DatabaseDriver $dba, ModelDefinition $model) { $this->dba = $dba; $this->model = $model; $this->iterator = $result; } /** * The current function of the iterator, called in each loop. This function returns * a cached version of the Record decorator class for the current row in the database * result set. * @return mixed A cached instance of Record * @see Record * @internal */ final public function current() { if(!isset($this->cache[$this->key()])) { $record = camelize($model->table['name']) .'Record'; $this->cache[$this->key()] = new $record($this->iterator->current(), $this->dba, $this->model); } return $this->cache[$this->key()]; } /** * Return the number of rows in the internal result set * @see DatabaseResultSet::numRows() */ final public function numRows() { return $this->iterator->numRows(); } }