All pastes #594817 Raw Edit

k4st

public php v1 · immutable
#594817 ·published 2007-06-29 03:26 UTC
rendered paste body
<?php/** * $header * @copyright Copyright 2007, Peter Goodman * @package valkyrie * @subpackage Database.activerecord * @author Peter Goodman */if(!defined('IN_VALKYRIE')) {	exit;}/** * A decorator class for an individual row in the database. This class extends * VK_Overload. */class VK_DatabaseRecord extends VK_Overload implements VK_Record, IteratorAggregate {		/**	 * A reference to the database abstraction layer	 * @see Database	 * @internal	 */	protected $dba;		/**	 * A reference to this table's model definition	 * @see ModelDefinition	 * @internal	 */	private $model;		/**	 * A boolean of whether or not this row/record exists in the database yet.	 * @internal	 */	private $saved = FALSE;		/**	 * An array holding saved data from the database. If a database row for this	 * record does not exist, this array will be empty.	 * @internal	 */	private $data = array();		/**	 * An array of all user-set values to this table row that don't exist in the database	 * yet and haven't been validated.	 * @internal	 */	private $dirty = array();		/**	 * A cache of the return values of getter functions in this class. all functions prefixed	 * by 'get' are available to the template engine via {$record.func_name} (refers to getFuncName)	 * @internal	 */	private $cache = array();		/**	 * Constructor, bring in references to the database abstraction layer, model definition, and an	 * array of saved data. If that array is empty, then VK_DatabaseRecord::$saved will remain false.	 * @param array $data An array containing initial saved data (if the row is saved).	 * @param mixed $dba A reference to the database abstractin layer.	 * @param mixed $model A reference to the model definition for this database table.	 * @see Database, ModelDefinition	 * @internal	 */	final public function __construct(array $data = array(), DatabaseDriver $dba, DatabaseModelDefinition $definition) {		$this->dba = $dba;		$this->definition = $definition;				if(!empty($data)) {			$this->saved = TRUE;		}				$this->data = $data;	}		/**	 * Quick accessor for the value of the primary key.	 */	final public function getId() {		return isset($this->data[$this->definition->getPrimaryKey()]) ? $this->data[$this->definition->getPrimaryKey()] : FALSE;	}		/**	 * 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 VK_DatabaseRecord::__set(), VK_DatabaseRecord::__isset(), VK_DatabaseRecord::__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);						// call a function			if(is_callable($this, $method_name)) {								$ret = $this->cache[$key] = $this->$method_name();							// get a relation			} else if($relation = $this->definition->getRelation($key)) {				echo "\n\n". get_class($relation) ."\n\n";				$finder = $relation->getModel()->getFinder($relation->getModelId());				$func = $relation instanceof VK_ToOneRelation ? 'findByQuery' : 'findAllByQuery';				$ret = $this->cache[$key] = $finder->$func($relation->getQuery(), array($this->getId()));			}		}				return $ret;	}		/**	 * Call a relation so that extra parameters can be passed to it.	 */	public function __call($key, array $args = array()) {		if(!isset($this->cache[$key]) && isset($args[0]) && $relation = $this->definition->getRelation($key)) {						call_user_func_array(array($relation, 'setSelect'), $args);						$finder = $relation->getModel()->getFinder($relation->getModelId());			$func = $relation instanceof ToOneRelation ? 'findByQuery' : 'findAllByQuery';							$this->cache[$key] = $finder->$func($relation->getQuery(), array($this->getId()));		}				return isset($this->cache[$key]) ? $this->cache[$key] : NULL;	}		/**	 * 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 VK_DatabaseRecord::__get(), VK_DatabaseRecord::__isset(), VK_DatabaseRecord::__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	 * @todo Do I want to have to pre-fetch here? It's the only way for it to work with the template engine :(	 * @see VK_Overload	 * @see VK_DatabaseRecord::__get(), VK_DatabaseRecord::__set(), VK_DatabaseRecord::__unset()	 */	final public function __isset($key = '') {		//return isset($this->dirty[$key]) || isset($this->data[$key]) || isset($this->cache[$key]);		return $this->__get($key) !== NULL;	}		/**	 * Unset the value for a column name	 * @param string $key The key (column name) to unset	 * @see VK_Overload	 * @see VK_DatabaseRecord::__get(), VK_DatabaseRecord::__set(), VK_DatabaseRecord::__isset()	 */	final public function __unset($key = '') {		unset($this->dirty[$key], $this->data[$key]);	}		/**	 * Bind an array to the dirty values.	 */	final public function bindArray($array = array()) {		if(is_array($array)) {			$this->dirty = array_merge($this->dirty, $array);		} else if($array instanceof DatabaseRecord) {			foreach($array as $key => $val) {				$this->$key = $val;			}		}	}		/**	 * Get a database finder.	 */	final public function getFinder($id) {		return $this->definition->getModel()->getFinder($id);	}		/**	 * 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() { }		/**	 * Validate the dirty fields.	 * @see VK_ModelDefinition::validateField()	 * @internal	 */	final private function validate() {		foreach($this->dirty as $field => $val) {			if(FALSE !== ($val = $this->definition->validateField($field, $val))) {				$this->dirty[$field] = $val;			} else {				unset($this->dirty[$field]);			}		}	}		/**	 * 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() {				// call the pre-save hook		$this->preSave();				$insert_vals = array();				// validate the dirty things against the model		$this->validate();				// get dirty things		$keys = array_keys($this->dirty);		$vals = array_values($this->dirty);						// update		if($this->saved) {			$sql = 'UPDATE '. $this->definition->getTable('name') .' SET ';						$sep = '';			foreach($keys as $key) {				$sql .= $sep . $key .'=?';				$sep = ',';			}						$sql .= ' WHERE '. $this->definition->getPrimaryKey() .'=?';						$vals[] = $this->getId();						$this->dba->update($sql, $vals);				// insert		} else {			$this->dba->update($this->dba->buildInsert($this->definition->getTable('name'), $keys, $vals), $vals);						if($this->definition->getPrimaryKey() !== NULL) {				$this->data[$this->definition->getPrimaryKey()] = $this->dba->insertId();			}		}					$this->saved = TRUE;		$this->data = array_merge($this->data, $this->dirty);		$this->dirty = array();				// call the post-save hook		$this->postSave();				return TRUE;	}		/**	 * Delete a record from the database if it exists there already.	 */	final public function delete() {		if($this->saved) {			if($this->definition->getPrimaryKey() !== NULL) {				$this->dba->update('DELETE FROM '. $this->definition->getTable('name') .' WHERE '. $this->definition->getPrimaryKey() .'=?', array($this->getId()));			} else {				// hrm.			}		}	}		/**	 * Iterate through the dirty/data arrays.	 */	public function getIterator() {		return new ArrayIterator(array_merge($this->data, $this->dirty));	}}/** * A class it iterate over a result set. Each iteration will return a VK_DatabaseRecord * decorator class for the row. This class extends IteratorIterator because the * database result sets passed in implement Iterator. * @see VK_DatabaseRecord, IteratorIterator, DatabaseResultSet */class VK_DatabaseRecordSet extends IteratorIterator implements VK_RecordSet, Countable {		/**	 * A reference to the current database abstraction layer	 * @see Database	 * @internal	 */	private $dba;		/**	 * A reference to this tables model definition	 * @see DatabaseModelDefinition	 * @internal	 */	private $definition;		/**	 * 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, DatabaseModelDefinition $definition) {		$this->dba = $dba;		$this->definition = $definition;				parent::__construct($result);	}		/**	 * The current function of the iterator, called in each loop. This function returns	 * a cached version of the VK_DatabaseRecord decorator class for the current row in the database	 * result set.	 * @return mixed A cached instance of VK_DatabaseRecord	 * @see VK_DatabaseRecord	 * @internal	 */	public function current() {		if(!isset($this->cache[$this->key()])) {			$record = $this->definition->getName() .'Record';			$record = class_exists($record, FALSE) ? $record : 'VK_DatabaseRecord';			$this->cache[$this->key()] = new $record(parent::current(), $this->dba, $this->definition);		}				return $this->cache[$this->key()];	}		/**	 * Return the number of rows in the internal result set	 * @see DatabaseResultSet::numRows()	 */	final public function numRows() {		return $this->getInnerIterator()->numRows();	}		/**	 * Return the number of rows.	 */	final public function count() {		return $this->numRows();	}		/**	 * Delete all items in this record set, also cause a cascade delete if necessary.	 */	final public function delete() {			}}