From f6fac5cc2f679a6e93a39ea4127f58e438a583c5 Mon Sep 17 00:00:00 2001 From: wei <> Date: Thu, 1 Nov 2007 04:20:14 +0000 Subject: Refactored ActiveRecord: removed object registry, fixed relationship casing problems. --- framework/Data/ActiveRecord/TActiveRecord.php | 173 +++++++++++++++++++++----- 1 file changed, 141 insertions(+), 32 deletions(-) (limited to 'framework/Data/ActiveRecord/TActiveRecord.php') diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index f5d73582..4bbee046 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -108,6 +108,27 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext'); * fetched in a lazy way, which avoids unnecessary overhead if the foreign objects are not accessed * at all. * + * Since v3.1.2, new events OnInsert, OnUpdate and OnDelete are available. + * The event OnInsert, OnUpdate and OnDelete methods are executed before + * inserting, updating, and deleting the current record, respectively. You may override + * these methods; a TActiveRecordChangeEventParameter parameter is passed to these methods. + * The property {@link TActiveRecordChangeEventParameter::setIsValid IsValid} of the parameter + * can be set to false to prevent the change action to be executed. This can be used, + * for example, to validate the record before the action is executed. For example, + * in the following the password property is hashed before a new record is inserted. + * + * class UserRecord extends TActiveRecord + * { + * function OnInsert($param) + * { + * //parent method should be called to raise the event + * parent::OnInsert($param); + * $this->nounce = md5(time()); + * $this->password = md5($this->password.$this->nounce); + * } + * } + * + * * @author Wei Zhuo * @version $Id$ * @package System.Data.ActiveRecord @@ -120,6 +141,16 @@ abstract class TActiveRecord extends TComponent const HAS_MANY='HAS_MANY'; const MANY_TO_MANY='MANY_TO_MANY'; + const STATE_NEW=0; + const STATE_LOADED=1; + const STATE_DELETED=2; + + /** + * @var integer object state: 0 = new, 1 = loaded, 2 = deleted. + * @since 3.1.2 + */ + private $_objectState=0; + /** * This static variable defines the column mapping. * The keys are physical column names as defined in database, @@ -177,11 +208,12 @@ abstract class TActiveRecord extends TComponent */ public function __construct($data=array(), $connection=null) { - $this->copyFrom($data); if($connection!==null) - $this->_connection=$connection; + $this->setDbConnection($connection); $this->setupColumnMapping(); $this->setupRelations(); + if(!empty($data)) //$data may be an object + $this->copyFrom($data); } /** @@ -242,7 +274,7 @@ abstract class TActiveRecord extends TComponent $class=new ReflectionClass($className); $relations=array(); foreach($class->getStaticPropertyValue('RELATIONS') as $key=>$value) - $relations[strtolower($key)]=$value; + $relations[strtolower($key)]=array($key,$value); self::$_relations[$className]=$relations; } } @@ -258,7 +290,7 @@ abstract class TActiveRecord extends TComponent if(!is_array($data)) throw new TActiveRecordException('ar_must_copy_from_array_or_object', get_class($this)); foreach($data as $name=>$value) - $this->$name = $value; + $this->setColumnValue($name,$value); return $this; } @@ -270,11 +302,7 @@ abstract class TActiveRecord extends TComponent public function getDbConnection() { if($this->_connection===null) - { - $this->setDbConnection(self::getRecordManager()->getDbConnection()); - if($this->_connection===null) //check it - throw new TActiveRecordException('ar_invalid_db_connection',get_class($this)); - } + return self::getRecordManager()->getDbConnection(); return $this->_connection; } @@ -367,19 +395,31 @@ abstract class TActiveRecord extends TComponent } /** - * Commit changes to the record, may insert, update or delete depending - * on the record state given in TObjectStateRegistery. + * Commit changes to the record: insert, update or delete depending on the object state. * @return boolean true if changes were made. */ protected function commitChanges() { - $registry = $this->getRecordManager()->getObjectStateRegistry(); $gateway = $this->getRecordGateway(); if(!$this->_readOnly) $this->_readOnly = $gateway->getRecordTableInfo($this)->getIsView(); if($this->_readOnly) throw new TActiveRecordException('ar_readonly_exception',get_class($this)); - return $registry->commit($this,$gateway); + $param = new TActiveRecordChangeEventParameter(); + switch($this->_objectState) + { + case self::STATE_NEW: + $this->onInsert($param); + return $param->getIsValid() ? $gateway->insert($this) : false; + case self::STATE_LOADED: + $this->onUpdate($param); + return $param->getIsValid() ? $gateway->update($this) : false; + case self::STATE_DELETED: + $this->onDelete($param); + return $param->getIsValid() ? $gateway->delete($this) : false; + default: + throw new TActiveRecordException('ar_invalid_state', get_class($this)); + } } /** @@ -389,8 +429,7 @@ abstract class TActiveRecord extends TComponent */ public function delete() { - $registry = $this->getRecordManager()->getObjectStateRegistry(); - $registry->registerRemoved($this); + $this->_objectState = self::STATE_DELETED; return $this->commitChanges(); } @@ -456,19 +495,22 @@ abstract class TActiveRecord extends TComponent { if(empty($data)) return null; - //create and populate the object + $obj = self::createRecordInstance($type, $data, self::STATE_LOADED); + return $obj; + } + + /** + * Create an instance of ActiveRecord class given by $type. + * This static method should only be used internally within core ActiveRecord classes. + */ + public static function createRecordInstance($type, $data=array(), $state=self::STATE_NEW) + { $obj = Prado::createComponent($type); - $tableInfo = $this->getRecordGateway()->getRecordTableInfo($obj); - foreach($data as $name=>$value) - $obj->setColumnValue($name,$value); - /* - foreach($tableInfo->getColumns()->getKeys() as $name) - { - if(isset($data[$name])) - $obj->setColumnValue($name,$data[$name]); - }*/ - $obj->_readOnly = $tableInfo->getIsView(); - $this->getRecordManager()->getObjectStateRegistry()->registerClean($obj); + $obj->_objectState=$state; + $tableInfo = $obj->getRecordGateway()->getRecordTableInfo($obj); + $obj->_readOnly=$tableInfo->getIsView(); + if(!empty($data)) + $obj->copyFrom($data); return $obj; } @@ -642,9 +684,9 @@ abstract class TActiveRecord extends TComponent * @param array method call arguments. * @return TActiveRecordRelation, null if the context or the handler doesn't exist */ - protected function getRelationHandler($property,$args=array()) + protected function getRelationHandler($name,$args=array()) { - if(($context=$this->getRelationContext($property)) !== null) + if(($context=$this->getRelationContext($name)) !== null) { $criteria = $this->getCriteria(count($args)>0 ? $args[0] : null, array_slice($args,1)); return $context->getRelationHandler($criteria); @@ -662,10 +704,10 @@ abstract class TActiveRecord extends TComponent * the active record relationships for given property, null if invalid relationship * @since 3.1.2 */ - protected function getRelationContext($property) + protected function getRelationContext($name) { - if(($relation=$this->getRelation($property))!==null) - return new TActiveRecordRelationContext($this,strtolower($property),$relation); + if(list($property, $relation) = $this->getRelation($name)) + return new TActiveRecordRelationContext($this,$property,$relation); else return null; } @@ -824,6 +866,36 @@ abstract class TActiveRecord extends TComponent $this->raiseEvent('OnExecuteCommand', $this, $param); } + /** + * Raised before the record attempt to insert its data into the database. + * To prevent the insert operation, set the TActiveRecordChangeEventParameter::IsValid parameter to false. + * @param TActiveRecordChangeEventParameter event parameter to be passed to the event handlers + */ + public function onInsert($param) + { + $this->raiseEvent('OnInsert', $this, $param); + } + + /** + * Raised before the record attempt to delete its data from the database. + * To prevent the insert operation, set the TActiveRecordChangeEventParameter::IsValid parameter to false. + * @param TActiveRecordChangeEventParameter event parameter to be passed to the event handlers + */ + public function onDelete($param) + { + $this->raiseEvent('OnDelete', $this, $param); + } + + /** + * Raised before the record attempt to update its data in the database. + * To prevent the insert operation, set the TActiveRecordChangeEventParameter::IsValid parameter to false. + * @param TActiveRecordChangeEventParameter event parameter to be passed to the event handlers + */ + public function onUpdate($param) + { + $this->raiseEvent('OnUpdate', $this, $param); + } + /** * Retrieves the column value according to column name. * This method is used internally. @@ -885,4 +957,41 @@ abstract class TActiveRecord extends TComponent return isset(self::$_relations[get_class($this)][strtolower($property)]); } } + +/** + * TActiveRecordChangeEventParameter class + * + * TActiveRecordChangeEventParameter encapsulates the parameter data for + * ActiveRecord change commit events that are broadcasted. The following change events + * may be raise: {@link TActiveRecord::OnInsert}, {@link TActiveRecord::OnUpdate} and + * {@link TActiveRecord::OnDelete}. The {@link setIsValid IsValid} parameter can + * be set to false to prevent the requested change event to be performed. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1.2 + */ + +class TActiveRecordChangeEventParameter extends TEventParameter +{ + private $_isValid=true; + + /** + * @return boolean whether the event should be performed. + */ + public function getIsValid() + { + return $this->_isValid; + } + + /** + * @param boolean set to false to prevent the event. + */ + public function setIsValid($value) + { + $this->_isValid = TPropertyValue::ensureBoolean($value); + } +} + ?> \ No newline at end of file -- cgit v1.2.3