From a8b3ebe8f62c3888b216d827c1c5dcba8a47d4e1 Mon Sep 17 00:00:00 2001 From: wei <> Date: Sun, 26 Nov 2006 22:15:58 +0000 Subject: Adding active record implementation. --- .../ActiveRecord/TActiveRecordStateRegistry.php | 262 +++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 framework/Data/ActiveRecord/TActiveRecordStateRegistry.php (limited to 'framework/Data/ActiveRecord/TActiveRecordStateRegistry.php') diff --git a/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php b/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php new file mode 100644 index 00000000..4493d4cb --- /dev/null +++ b/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php @@ -0,0 +1,262 @@ + + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +/** + * Active record Unit of Work class and Identity Map. + * + * Maintains a list of objects affected by a business transaction and + * coordinates the writing out of changes and the resolution of concurrency problems. + * + * This registry keeps track of everything you do during a business transaction + * that can affect the database. When you're done, it figures out everything that + * needs to be done to alter the database as a result of your work. + * + * The object can only be in one of the four states: "new", "clean", "dirty" or "removed". + * A "new" object is one that is created not by loading the record from database. + * A "clean" object is one that is created by loading the record from datase. + * A "dirty" object is one that is marked as dirty or a "clean" object that has + * its internal state altered (done by using == object comparision). + * A "removed" object is one that is marked for deletion. + * + * See the "Active Record Object States.png" in the docs directory for state + * transition diagram. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordStateRegistry +{ + private $_cleanObjects=array(); + private $_removedObjects; + private $_cachedObjects=array(); + /** + * Initialize the registry. + */ + public function __construct() + { + $this->_removedObjects = new TList; + } + + /** + * Get the cached object for given type and row data. The cached object + * must also be clean. + * @param mixed row data fetched + * @return TActiveRecord cached object if found, null otherwise. + */ + public function getCachedInstance($data) + { + $key = $this->getObjectDataKey($data); + if(isset($this->_cachedObjects[$key])) + { + $obj = $this->_cachedObjects[$key]; + if($this->getIsCleanObject($obj)) + return $obj; + } + } + + /** + * Cache the object that corresponding to the fetched row data. + * @param mixed row data fetched. + * @param TActiveRecord object to be cached. + * @return TActiveRecord cached object. + */ + public function addCachedInstance($data,$obj) + { + $key = $this->getObjectDataKey($data); + $this->registerClean($obj); + $this->_cachedObjects[$key]=$obj; + return $obj; + } + + /** + * @return string hash of the data. + */ + protected function getObjectDataKey($data) + { + return sprintf('%x',crc32(serialize($data))); + } + + /** + * Ensure that object is not null. + */ + protected function assertNotNull($obj) + { + if(is_null($obj)) + throw new TActiveRecordException('ar_object_must_not_be_null'); + } + + /** + * Register the object for deletion, when the object invokes its delete() method + * the corresponding row in the database is deleted. + * @param TActiveRecord existing active record. + * @throws TActiveRecordException if object is null. + */ + public function registerRemoved($obj) + { + $this->assertNotNull($obj); + $found=false; + foreach($this->_cleanObjects as $i=>$cache) + { + if($cache[0]===$obj) + { + unset($this->_cleanObjects[$i]); + $found=true; + } + } + if(!$found) + throw new TActiveRecordException('ar_object_must_be_retrieved_before_delete'); + if(!$this->_removedObjects->contains($obj)) + $this->_removedObjects->add($obj); + } + + /** + * Register a clean object attached to a specific data that was used to + * populate the object. This acts as an object cache. + * @param TActiveRecord new clean object. + */ + public function registerClean($obj) + { + $this->removeCleanOrDirty($obj); + if($this->getIsRemovedObject($obj)) + throw new TActiveRecordException('ar_object_marked_for_removal'); + $this->_cleanObjects[] = array($obj, clone($obj)); + } + + /** + * Remove the object from dirty state. + * @param TActiveRecord object to remove. + */ + protected function removeDirty($obj) + { + $this->assertNotNull($obj); + foreach($this->_cleanObjects as $i=>$cache) + if($cache[0]===$obj && $obj != $cache[1]) + unset($this->_cleanObjects[$i]); + } + + /** + * Remove object from clean state. + * @param TActiveRecord object to remove. + */ + protected function removeClean($obj) + { + $this->assertNotNull($obj); + foreach($this->_cleanObjects as $i=>$cache) + if($cache[0]===$obj && $obj == $cache[1]) + unset($this->_cleanObjects[$i]); + } + + /** + * Remove object from dirty and clean state. + * @param TActiveRecord object to remove. + */ + protected function removeCleanOrDirty($obj) + { + $this->assertNotNull($obj); + foreach($this->_cleanObjects as $i=>$cache) + if($cache[0]===$obj) + unset($this->_cleanObjects[$i]); + } + + /** + * Remove object from removed state. + * @param TActiveRecord object to remove. + */ + protected function removeRemovedObject($obj) + { + $this->_removedObjects->remove($obj); + } + + /** + * Test whether an object is dirty or has been modified. + * @param TActiveRecord object to test. + * @return boolean true if the object is dirty, false otherwise. + */ + public function getIsDirtyObject($obj) + { + foreach($this->_cleanObjects as $cache) + if($cache[0] === $obj) + return $obj != $cache[1]; + return false; + } + + /** + * Test whether an object is in the clean state. + * @param TActiveRecord object to test. + * @return boolean true if object is clean, false otherwise. + */ + public function getIsCleanObject($obj) + { + foreach($this->_cleanObjects as $cache) + if($cache[0] === $obj) + return $obj == $cache[1]; + return false; + } + + /** + * Test whether an object is a new instance. + * @param TActiveRecord object to test. + * @return boolean true if object is newly created, false otherwise. + */ + public function getIsNewObject($obj) + { + if($this->getIsRemovedObject($obj)) return false; + foreach($this->_cleanObjects as $cache) + if($cache[0] === $obj) + return false; + return true; + } + + /** + * Test whether an object is marked for deletion. + * @param TActiveRecord object to test. + * @return boolean true if object is marked for deletion, false otherwise. + */ + public function getIsRemovedObject($obj) + { + return $this->_removedObjects->contains($obj); + } + + /** + * Commit the object to database: + * * a new record is inserted if the object is new, object becomes clean. + * * the record is updated if the object is dirty, object becomes clean. + * * the record is deleted if the object is marked for removal. + * + * @param TActiveRecord record object. + * @param TActiveRecordGateway database gateway + * @return boolean true if commit was successful, false otherwise. + */ + public function commit($record,$gateway) + { + $rowsAffected=0; + + if($this->getIsRemovedObject($record)) + { + $rowsAffected = $gateway->delete($record); + if($rowsAffected===1) + $this->removeRemovedObject($record); + } + else + { + if($this->getIsDirtyObject($record)) + $rowsAffected = $gateway->update($record); + else if($this->getIsNewObject($record)) + $rowsAffected = $gateway->insert($record); + + if($rowsAffected===1) + $this->registerClean($record); + } + return $rowsAffected===1; + } +} + +?> \ No newline at end of file -- cgit v1.2.3