From 8708f7e88e423b44ea4b3b8fff14f055d2b5c4ac Mon Sep 17 00:00:00 2001 From: wei <> Date: Fri, 4 May 2007 08:37:12 +0000 Subject: Add saving/updating ActiveRecord relationships. --- .gitattributes | 2 + .../protected/pages/Database/ActiveRecord.page | 14 +- .../Data/ActiveRecord/Exceptions/messages.txt | 3 +- .../Relations/TActiveRecordBelongsTo.php | 24 +++ .../Relations/TActiveRecordHasMany.php | 27 +++ .../Relations/TActiveRecordHasManyAssociation.php | 132 ++++++++++++- .../ActiveRecord/Relations/TActiveRecordHasOne.php | 19 ++ .../Relations/TActiveRecordRelation.php | 8 +- .../Relations/TActiveRecordRelationContext.php | 59 ++++-- framework/Data/ActiveRecord/TActiveRecord.php | 5 +- .../Data/ActiveRecord/TActiveRecordGateway.php | 13 +- .../ActiveRecord/TActiveRecordStateRegistry.php | 27 ++- framework/Data/Common/Sqlite/TSqliteMetaData.php | 3 +- framework/Data/Common/Sqlite/TSqliteTableInfo.php | 8 + .../ActiveRecord/ActiveRecordRegistryTestCase.php | 83 ++++---- .../ActiveRecord/ForeignKeyTestCase.php | 15 ++ .../ActiveRecord/ForeignObjectUpdateTest.php | 211 +++++++++++++++++++++ .../ActiveRecord/records/ItemRecord.php | 49 +++++ .../simple_unit/SqlMap/ActiveRecordSqlMapTest.php | 6 +- 19 files changed, 624 insertions(+), 84 deletions(-) create mode 100644 tests/simple_unit/ActiveRecord/ForeignObjectUpdateTest.php create mode 100644 tests/simple_unit/ActiveRecord/records/ItemRecord.php diff --git a/.gitattributes b/.gitattributes index 1540fabf..333c1d80 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2662,6 +2662,7 @@ tests/simple_unit/ActiveRecord/DeleteByPkTestCase.php -text tests/simple_unit/ActiveRecord/FindByPksTestCase.php -text tests/simple_unit/ActiveRecord/FindBySqlTestCase.php -text tests/simple_unit/ActiveRecord/ForeignKeyTestCase.php -text +tests/simple_unit/ActiveRecord/ForeignObjectUpdateTest.php -text tests/simple_unit/ActiveRecord/RecordEventTestCase.php -text tests/simple_unit/ActiveRecord/SqliteTestCase.php -text tests/simple_unit/ActiveRecord/UserRecordTestCase.php -text @@ -2673,6 +2674,7 @@ tests/simple_unit/ActiveRecord/mysql4text.sql -text tests/simple_unit/ActiveRecord/records/Blogs.php -text tests/simple_unit/ActiveRecord/records/DepSections.php -text tests/simple_unit/ActiveRecord/records/DepartmentRecord.php -text +tests/simple_unit/ActiveRecord/records/ItemRecord.php -text tests/simple_unit/ActiveRecord/records/SimpleUser.php -text tests/simple_unit/ActiveRecord/records/SqliteUsers.php -text tests/simple_unit/ActiveRecord/records/UserRecord.php -text diff --git a/demos/quickstart/protected/pages/Database/ActiveRecord.page b/demos/quickstart/protected/pages/Database/ActiveRecord.page index adcb7783..1e3c69c5 100644 --- a/demos/quickstart/protected/pages/Database/ActiveRecord.page +++ b/demos/quickstart/protected/pages/Database/ActiveRecord.page @@ -547,9 +547,9 @@ class PlayerRecord extends TActiveRecord protected static $RELATIONS=array ( - 'team' => array(SELF::BELONGS_TO, 'TeamRecord'), - 'skills' => array(SELF::HAS_MANY, 'SkillRecord', 'Player_Skills'), - 'profile' => array(SELF::HAS_ONE, 'ProfileRecord'), + 'team' => array(self::BELONGS_TO, 'TeamRecord'), + 'skills' => array(self::HAS_MANY, 'SkillRecord', 'Player_Skills'), + 'profile' => array(self::HAS_ONE, 'ProfileRecord'), ); public static function finder($className=__CLASS__) @@ -572,7 +572,7 @@ class ProfileRecord extends TActiveRecord protected static $RELATIONS=array ( - 'player' => array(SELF::BELONGS_TO, 'PlayerRecord'), + 'player' => array(self::BELONGS_TO, 'PlayerRecord'), ); public static function finder($className=__CLASS__) @@ -589,14 +589,14 @@ class ProfileRecord extends TActiveRecord class SkillRecord extends TActiveRecord { const TABLE='Skills'; - public $player_id; - public $salary; + public $skill_id; + public $name; public $players=array(); protected static $RELATIONS=array ( - 'players' => array(SELF::HAS_MANY, 'PlayerRecord', 'Player_Skills'), + 'players' => array(self::HAS_MANY, 'PlayerRecord', 'Player_Skills'), ); public static function finder($className=__CLASS__) diff --git a/framework/Data/ActiveRecord/Exceptions/messages.txt b/framework/Data/ActiveRecord/Exceptions/messages.txt index 6c13450d..fabfc1a4 100644 --- a/framework/Data/ActiveRecord/Exceptions/messages.txt +++ b/framework/Data/ActiveRecord/Exceptions/messages.txt @@ -20,4 +20,5 @@ ar_invalid_finder_class_name = Class name for finder($className) method must ar_invalid_criteria = Invalid criteria object, must be a string or instance of TSqlCriteria. ar_relations_undefined = Unable to determine Active Record relationships because static array property {0}::${1} is not defined. ar_undefined_relation_prop = Unable to find {1}::${2}['{0}'], Active Record relationship definition for property "{0}" not found in entries of {1}::${2}. -ar_invalid_relationship = Invalid active record relationship. \ No newline at end of file +ar_invalid_relationship = Invalid active record relationship. +ar_relations_missing_fk = Unable to find foreign key relationships in table '{0}' that corresponds to table '{1}'. \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php index 1168bf55..9374af64 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php @@ -104,6 +104,30 @@ class TActiveRecordBelongsTo extends TActiveRecordRelation $source->{$prop} = $collections[$hash][0]; } } + + /** + * Updates the source object first. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObject = $obj->{$this->getContext()->getProperty()}; + $registry = $fkObject->getRecordManager()->getObjectStateRegistry(); + if($registry->shouldPersistObject($fkObject)) + { + if($fkObject!==null) + { + $fkObject->save(); + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($source, $fkObject); + foreach($fkeys as $srcKey => $fKey) + $source->{$srcKey} = $fkObject->{$fKey}; + return true; + } + } + return true; + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php index ab28a455..d70f911b 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php @@ -85,6 +85,33 @@ class TActiveRecordHasMany extends TActiveRecordRelation $fkObjects = $this->findForeignObjects($fields,$indexValues); $this->populateResult($results,$properties,$fkObjects,$fields); } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObjects = &$obj->{$this->getContext()->getProperty()}; + $success=true; + if(($total = count($fkObjects))> 0) + { + $source = $this->getSourceRecord(); + $registry = $source->getRecordManager()->getObjectStateRegistry(); + $fkeys = $this->findForeignKeys($fkObjects[0], $source); + for($i=0;$i<$total;$i++) + { + if($registry->shouldPersistObject($fkObjects[$i])) + { + foreach($fkeys as $fKey => $srcKey) + $fkObjects[$i]->{$fKey} = $source->{$srcKey}; + $success = $success && $fkObjects[$i]->save(); + } + } + } + return $success; + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php index 50558a2b..2dee2bcd 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php @@ -87,6 +87,7 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation private $_association; private $_sourceTable; private $_foreignTable; + private $_association_columns=array(); /** * Get the foreign key index values from the results and make calls to the @@ -117,8 +118,14 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation { $gateway = $this->getSourceRecord()->getRecordGateway(); $conn = $this->getSourceRecord()->getDbConnection(); - $table = $this->getContext()->getAssociationTable(); - $this->_association = $gateway->getTableInfo($conn, $table); + //table name may include the fk column name separated with a dot. + $table = explode('.', $this->getContext()->getAssociationTable()); + if(count($table)>1) + { + $columns = preg_replace('/^\((.*)\)/', '\1', $table[1]); + $this->_association_columns = preg_split('/\s*[, ]\*/',$columns); + } + $this->_association = $gateway->getTableInfo($conn, $table[0]); } return $this->_association; } @@ -158,6 +165,13 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord()); } + protected function getForeignCommandBuilder() + { + $obj = $this->getContext()->getForeignRecordFinder(); + return $this->getSourceRecord()->getRecordGateway()->getCommand($obj); + } + + /** * Fetches the foreign objects using TActiveRecord::findAllByIndex() * @param array field names @@ -172,12 +186,12 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys); $srcProps = array_keys($sourceKeys); $collections=array(); - foreach($command->query() as $row) + foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row) { $hash = $this->getObjectHash($row, $srcProps); foreach($srcProps as $column) unset($row[$column]); - $obj = new $type($row); + $obj = $this->createFkObject($type,$row,$foreignKeys); $collections[$hash][] = $obj; $registry->registerClean($obj); } @@ -185,6 +199,24 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation $this->setResultCollection($results, $collections, array_values($sourceKeys)); } + /** + * @param string active record class name. + * @param array row data + * @param array foreign key column names + * @return TActiveRecord + */ + protected function createFkObject($type,$row,$foreignKeys) + { + $obj = new $type($row); + if(count($this->_association_columns) > 0) + { + $i=0; + foreach($foreignKeys as $ref=>$fk) + $obj->{$ref} = $row[$this->_association_columns[$i++]]; + } + return $obj; + } + /** * @param TSqlCriteria * @param TTableInfo association table info @@ -205,7 +237,7 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation $limit = $criteria->getLimit(); $offset = $criteria->getOffset(); - $builder = $this->getCommandBuilder()->getBuilder(); + $builder = $this->getForeignCommandBuilder()->getBuilder(); $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset); $this->getCommandBuilder()->onCreateCommand($command, $criteria); return $command; @@ -220,7 +252,8 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation $columns=array(); $table = $this->getAssociationTable(); $tableName = $table->getTableFullName(); - foreach($sourceKeys as $name=>$fkName) + $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns); + foreach($columnNames as $name) $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName(); return implode(', ', $columns); } @@ -241,9 +274,14 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation $fkTable = $fkInfo->getTableFullName(); $joins = array(); + $hasAssociationColumns = count($this->_association_columns) > 0; + $i=0; foreach($foreignKeys as $ref=>$fk) { - $refField = $refInfo->getColumn($ref)->getColumnName(); + if($hasAssociationColumns) + $refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName(); + else + $refField = $refInfo->getColumn($ref)->getColumnName(); $fkField = $fkInfo->getColumn($fk)->getColumnName(); $joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}"; } @@ -251,5 +289,85 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation $index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues); return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}"; } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $obj = $this->getContext()->getSourceRecord(); + $fkObjects = &$obj->{$this->getContext()->getProperty()}; + $success=true; + if(($total = count($fkObjects))> 0) + { + $source = $this->getSourceRecord(); + $registry = $source->getRecordManager()->getObjectStateRegistry(); + $builder = $this->getAssociationTableCommandBuilder(); + for($i=0;$i<$total;$i++) + { + if($registry->shouldPersistObject($fkObjects[$i])) + $success = $success && $fkObjects[$i]->save(); + } + return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success; + } + return $success; + } + + protected function getAssociationTableCommandBuilder() + { + $conn = $this->getContext()->getSourceRecord()->getDbConnection(); + return $this->getAssociationTable()->createCommandBuilder($conn); + } + + protected function hasAssociationData($builder,$data) + { + $condition=array(); + $table = $this->getAssociationTable(); + foreach($data as $name=>$value) + $condition[] = $table->getColumn($name)->getColumnName().' = ?'; + $command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data)); + $result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar())); + return intval($result) > 0; + } + + protected function addAssociationData($builder,$data) + { + $command = $builder->createInsertCommand($data); + return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0; + } + + protected function updateAssociationTable($obj,$fkObjects, $builder) + { + $source = $this->getSourceRecordValues($obj); + $foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]); + $success=true; + foreach($fkObjects as $fkObject) + { + $data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject)); + if(!$this->hasAssociationData($builder,$data)) + $success = $this->addAssociationData($builder,$data) && $success; + } + return $success; + } + + protected function getSourceRecordValues($obj) + { + $sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj); + $indexValues = $this->getIndexValues(array_values($sourceKeys), $obj); + $data = array(); + $i=0; + foreach($sourceKeys as $name=>$srcKey) + $data[$name] = $indexValues[0][$i++]; + return $data; + } + + protected function getForeignObjectValues($foreignKeys,$fkObject) + { + $data=array(); + foreach($foreignKeys as $name=>$fKey) + $data[$name] = $fkObject->{$fKey}; + return $data; + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php index 6348f16b..7127d2ac 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php @@ -119,6 +119,25 @@ class TActiveRecordHasOne extends TActiveRecordRelation $source->{$prop} = $collections[$hash][0]; } } + + /** + * Updates the associated foreign objects. + * @return boolean true if all update are success (including if no update was required), false otherwise . + */ + public function updateAssociatedRecords() + { + $fkObject = $this->getContext()->getPropertyValue(); + $registry = $fkObject->getRecordManager()->getObjectStateRegistry(); + if($registry->shouldPersistObject($fkObject)) + { + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($fkObject, $source); + foreach($fkeys as $fKey => $srcKey) + $fkObject->{$fKey} = $source->{$srcKey}; + return $fkObject->save(); + } + return true; + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php index 46609095..4dc9743f 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php @@ -95,7 +95,9 @@ abstract class TActiveRecordRelation if($fkeys['table']===$matchingTableName) return $fkeys['keys']; } - throw new TActiveRecordException('no fk defined for '.$tableInfo->getTableFullName()); + $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName(); + throw new TActiveRecordException('ar_relations_missing_fk', + $tableInfo->getTableFullName(), $matching); } /** @@ -189,6 +191,10 @@ abstract class TActiveRecordRelation $prop = $this->getContext()->getProperty(); $source->{$prop} = isset($collections[$hash]) ? $collections[$hash] : array(); } + + public function updateAssociatedRecords() + { + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php index 167c90a5..e601eb53 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php @@ -35,38 +35,52 @@ class TActiveRecordRelationContext private $_criteria; private $_relation; - public function __construct($source, $property, $criteria) + public function __construct($source, $property=null, $criteria=null) { $this->_sourceRecord=$source; - $this->_property=$property; $this->_criteria=$criteria; - $this->_relation = $this->getSourceRecordRelation($property); + if($property!==null) + list($this->_property, $this->_relation) = $this->getSourceRecordRelation($property); } /** * Uses ReflectionClass to obtain the relation details array of a given * property from the $RELATIONS static property in TActiveRecord. * @param string relation property name - * @return array relation definition. + * @return array array($propertyName, $relation) relation definition. * @throws TActiveRecordException if property is not defined or missing. */ protected function getSourceRecordRelation($property) { - $class = new ReflectionClass($this->_sourceRecord); - $statics = $class->getStaticProperties(); - if(!isset($statics[self::RELATIONS_CONST])) - throw new TActiveRecordException('ar_relations_undefined', - get_class($this->_sourceRecord), self::RELATIONS_CONST); $property = strtolower($property); - foreach($statics[self::RELATIONS_CONST] as $name => $relation) + foreach($this->getRecordRelationships() as $name => $relation) { if(strtolower($name)===$property) - return $relation; + return array($name, $relation); } throw new TActiveRecordException('ar_undefined_relation_prop', $property, get_class($this->_sourceRecord), self::RELATIONS_CONST); } + /** + * @return array the key and values of TActiveRecord::$RELATIONS + */ + public function getRecordRelationships() + { + $class = new ReflectionClass($this->_sourceRecord); + $statics = $class->getStaticProperties(); + if(isset($statics[self::RELATIONS_CONST])) + return $statics[self::RELATIONS_CONST]; + else + return array(); + } + + public function getPropertyValue() + { + $obj = $this->getSourceRecord(); + return $obj->{$this->getProperty()}; + } + /** * @return string name of the record property that the relationship results will be assigned to. */ @@ -162,6 +176,29 @@ class TActiveRecordRelationContext throw new TActiveRecordException('ar_invalid_relationship'); } } + + /** + * @return TActiveRecordRelationCommand + */ + public function updateAssociatedRecords($updateBelongsTo=false) + { + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationCommand'); + $success=true; + foreach($this->getRecordRelationships() as $property=>$relation) + { + $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO; + if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo)) + { + $obj = $this->getSourceRecord(); + if(!empty($obj->{$property})) + { + $context = new self($this->getSourceRecord(),$property); + $success = $success && $context->getRelationHandler()->updateAssociatedRecords(); + } + } + } + return $success; + } } ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/TActiveRecord.php b/framework/Data/ActiveRecord/TActiveRecord.php index 479f643b..1f482e6a 100644 --- a/framework/Data/ActiveRecord/TActiveRecord.php +++ b/framework/Data/ActiveRecord/TActiveRecord.php @@ -316,11 +316,14 @@ abstract class TActiveRecord extends TComponent //create and populate the object $obj = Prado::createComponent($type); $tableInfo = $this->getRecordGateway()->getRecordTableInfo($obj); + foreach($data as $name=>$value) + $obj->{$name} = $value; + /* foreach($tableInfo->getColumns()->getKeys() as $name) { if(isset($data[$name])) $obj->{$name} = $data[$name]; - } + }*/ $obj->_readOnly = $tableInfo->getIsView(); $this->getRecordManager()->getObjectStateRegistry()->registerClean($obj); return $obj; diff --git a/framework/Data/ActiveRecord/TActiveRecordGateway.php b/framework/Data/ActiveRecord/TActiveRecordGateway.php index 40948999..d97844f7 100644 --- a/framework/Data/ActiveRecord/TActiveRecordGateway.php +++ b/framework/Data/ActiveRecord/TActiveRecordGateway.php @@ -244,9 +244,11 @@ class TActiveRecordGateway extends TComponent */ public function insert(TActiveRecord $record) { + $this->updateAssociatedRecords($record,true); $result = $this->getCommand($record)->insert($this->getInsertValues($record)); if($result) $this->updatePostInsert($record); + $this->updateAssociatedRecords($record); return $result; } @@ -297,8 +299,11 @@ class TActiveRecordGateway extends TComponent */ public function update(TActiveRecord $record) { + $this->updateAssociatedRecords($record,true); list($data, $keys) = $this->getUpdateValues($record); - return $this->getCommand($record)->updateByPk($data, $keys); + $result = $this->getCommand($record)->updateByPk($data, $keys); + $this->updateAssociatedRecords($record); + return $result; } protected function getUpdateValues(TActiveRecord $record) @@ -325,6 +330,12 @@ class TActiveRecordGateway extends TComponent return array($values,$primary); } + protected function updateAssociatedRecords(TActiveRecord $record,$updateBelongsTo=false) + { + $context = new TActiveRecordRelationContext($record); + return $context->updateAssociatedRecords($updateBelongsTo); + } + /** * Delete the record. * @param TActiveRecord record to be deleted. diff --git a/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php b/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php index d6f24961..7a285274 100644 --- a/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php +++ b/framework/Data/ActiveRecord/TActiveRecordStateRegistry.php @@ -97,7 +97,7 @@ class TActiveRecordStateRegistry public function registerClean($obj) { $this->removeCleanOrDirty($obj); - if($this->getIsRemovedObject($obj)) + if($this->isRemovedObject($obj)) throw new TActiveRecordException('ar_object_marked_for_removal'); $this->_cleanObjects[] = array($obj, clone($obj)); } @@ -152,7 +152,7 @@ class TActiveRecordStateRegistry * @param TActiveRecord object to test. * @return boolean true if the object is dirty, false otherwise. */ - public function getIsDirtyObject($obj) + public function isDirtyObject($obj) { foreach($this->_cleanObjects as $cache) if($cache[0] === $obj) @@ -165,7 +165,7 @@ class TActiveRecordStateRegistry * @param TActiveRecord object to test. * @return boolean true if object is clean, false otherwise. */ - public function getIsCleanObject($obj) + public function isCleanObject($obj) { foreach($this->_cleanObjects as $cache) if($cache[0] === $obj) @@ -178,21 +178,30 @@ class TActiveRecordStateRegistry * @param TActiveRecord object to test. * @return boolean true if object is newly created, false otherwise. */ - public function getIsNewObject($obj) + public function isNewObject($obj) { - if($this->getIsRemovedObject($obj)) return false; + if($this->isRemovedObject($obj)) return false; foreach($this->_cleanObjects as $cache) if($cache[0] === $obj) return false; return true; } + /** + * @param TActiveRecord object to test. + * @return boolean true if object is dirty or is new. + */ + public function shouldPersistObject($obj) + { + return $this->isDirtyObject($obj) || $this->isNewObject($obj); + } + /** * 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) + public function isRemovedObject($obj) { return $this->_removedObjects->contains($obj); } @@ -211,7 +220,7 @@ class TActiveRecordStateRegistry { $rowsAffected=false; - if($this->getIsRemovedObject($record)) + if($this->isRemovedObject($record)) { $rowsAffected = $gateway->delete($record); if($rowsAffected) @@ -219,9 +228,9 @@ class TActiveRecordStateRegistry } else { - if($this->getIsDirtyObject($record)) + if($this->isDirtyObject($record)) $rowsAffected = $gateway->update($record); - else if($this->getIsNewObject($record)) + else if($this->isNewObject($record)) $rowsAffected = $gateway->insert($record); if($rowsAffected) diff --git a/framework/Data/Common/Sqlite/TSqliteMetaData.php b/framework/Data/Common/Sqlite/TSqliteMetaData.php index 6c6ff232..1a72622e 100644 --- a/framework/Data/Common/Sqlite/TSqliteMetaData.php +++ b/framework/Data/Common/Sqlite/TSqliteMetaData.php @@ -33,6 +33,7 @@ class TSqliteMetaData extends TDbMetaData */ protected function createTableInfo($tableName) { + $tableName = str_replace("'",'',$tableName); $this->getDbConnection()->setActive(true); $table = $this->getDbConnection()->quoteString($tableName); $sql = "PRAGMA table_info({$table})"; @@ -49,7 +50,7 @@ class TSqliteMetaData extends TDbMetaData if($column->getIsPrimaryKey()) $primary[] = $col['name']; } - $info['TableName'] = $table; + $info['TableName'] = $tableName; if($this->getIsView($tableName)) $info['IsView'] = true; if(count($columns)===0) diff --git a/framework/Data/Common/Sqlite/TSqliteTableInfo.php b/framework/Data/Common/Sqlite/TSqliteTableInfo.php index e0bcb484..52c872d6 100644 --- a/framework/Data/Common/Sqlite/TSqliteTableInfo.php +++ b/framework/Data/Common/Sqlite/TSqliteTableInfo.php @@ -35,6 +35,14 @@ class TSqliteTableInfo extends TDbTableInfo Prado::using('System.Data.Common.Sqlite.TSqliteCommandBuilder'); return new TSqliteCommandBuilder($connection,$this); } + + /** + * @return string full name of the table, database dependent. + */ + public function getTableFullName() + { + return "'".$this->getTableName()."'"; + } } ?> \ No newline at end of file diff --git a/tests/simple_unit/ActiveRecord/ActiveRecordRegistryTestCase.php b/tests/simple_unit/ActiveRecord/ActiveRecordRegistryTestCase.php index 5ab89206..fbae5a4b 100644 --- a/tests/simple_unit/ActiveRecord/ActiveRecordRegistryTestCase.php +++ b/tests/simple_unit/ActiveRecord/ActiveRecordRegistryTestCase.php @@ -1,5 +1,4 @@ -assertTrue($registry->getIsNewObject($obj)); - $this->assertFalse($registry->getIsDirtyObject($obj)); - $this->assertFalse($registry->getIsRemovedObject($obj)); - $this->assertFalse($registry->getIsCleanObject($obj)); + $this->assertTrue($registry->isNewObject($obj)); + $this->assertFalse($registry->isDirtyObject($obj)); + $this->assertFalse($registry->isRemovedObject($obj)); + $this->assertFalse($registry->isCleanObject($obj)); } function test_clean_object_registers_without_error() @@ -28,10 +27,10 @@ class ActiveRecordRegistryTestCase extends UnitTestCase $registry = new TActiveRecordStateRegistry(); $registry->registerClean($obj); - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertFalse($registry->getIsDirtyObject($obj)); - $this->assertFalse($registry->getIsRemovedObject($obj)); - $this->assertTrue($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertFalse($registry->isDirtyObject($obj)); + $this->assertFalse($registry->isRemovedObject($obj)); + $this->assertTrue($registry->isCleanObject($obj)); } function test_clean_object_becomes_dirty_when_changed() @@ -43,10 +42,10 @@ class ActiveRecordRegistryTestCase extends UnitTestCase $obj->propB='b'; - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertTrue($registry->getIsDirtyObject($obj)); - $this->assertFalse($registry->getIsRemovedObject($obj)); - $this->assertFalse($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertTrue($registry->isDirtyObject($obj)); + $this->assertFalse($registry->isRemovedObject($obj)); + $this->assertFalse($registry->isCleanObject($obj)); } function test_removed_object_must_register_as_clean_first() @@ -73,10 +72,10 @@ class ActiveRecordRegistryTestCase extends UnitTestCase $registry->registerRemoved($obj); - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertFalse($registry->getIsDirtyObject($obj)); - $this->assertTrue($registry->getIsRemovedObject($obj)); - $this->assertFalse($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertFalse($registry->isDirtyObject($obj)); + $this->assertTrue($registry->isRemovedObject($obj)); + $this->assertFalse($registry->isCleanObject($obj)); } @@ -98,10 +97,10 @@ class ActiveRecordRegistryTestCase extends UnitTestCase $this->pass(); } - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertFalse($registry->getIsDirtyObject($obj)); - $this->assertTrue($registry->getIsRemovedObject($obj)); - $this->assertFalse($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertFalse($registry->isDirtyObject($obj)); + $this->assertTrue($registry->isRemovedObject($obj)); + $this->assertFalse($registry->isCleanObject($obj)); } function test_remove_dirty_object() @@ -113,17 +112,17 @@ class ActiveRecordRegistryTestCase extends UnitTestCase $obj->propB='b'; - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertTrue($registry->getIsDirtyObject($obj)); - $this->assertFalse($registry->getIsRemovedObject($obj)); - $this->assertFalse($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertTrue($registry->isDirtyObject($obj)); + $this->assertFalse($registry->isRemovedObject($obj)); + $this->assertFalse($registry->isCleanObject($obj)); $registry->registerRemoved($obj); - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertFalse($registry->getIsDirtyObject($obj)); - $this->assertTrue($registry->getIsRemovedObject($obj)); - $this->assertFalse($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertFalse($registry->isDirtyObject($obj)); + $this->assertTrue($registry->isRemovedObject($obj)); + $this->assertFalse($registry->isCleanObject($obj)); try { @@ -143,24 +142,24 @@ class ActiveRecordRegistryTestCase extends UnitTestCase $registry->registerClean($obj); - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertFalse($registry->getIsDirtyObject($obj)); - $this->assertFalse($registry->getIsRemovedObject($obj)); - $this->assertTrue($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertFalse($registry->isDirtyObject($obj)); + $this->assertFalse($registry->isRemovedObject($obj)); + $this->assertTrue($registry->isCleanObject($obj)); $obj->propB='b'; - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertTrue($registry->getIsDirtyObject($obj)); - $this->assertFalse($registry->getIsRemovedObject($obj)); - $this->assertFalse($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertTrue($registry->isDirtyObject($obj)); + $this->assertFalse($registry->isRemovedObject($obj)); + $this->assertFalse($registry->isCleanObject($obj)); $registry->registerClean($obj); - $this->assertFalse($registry->getIsNewObject($obj)); - $this->assertFalse($registry->getIsDirtyObject($obj)); - $this->assertFalse($registry->getIsRemovedObject($obj)); - $this->assertTrue($registry->getIsCleanObject($obj)); + $this->assertFalse($registry->isNewObject($obj)); + $this->assertFalse($registry->isDirtyObject($obj)); + $this->assertFalse($registry->isRemovedObject($obj)); + $this->assertTrue($registry->isCleanObject($obj)); } } diff --git a/tests/simple_unit/ActiveRecord/ForeignKeyTestCase.php b/tests/simple_unit/ActiveRecord/ForeignKeyTestCase.php index 8c39a797..0c1da5d2 100644 --- a/tests/simple_unit/ActiveRecord/ForeignKeyTestCase.php +++ b/tests/simple_unit/ActiveRecord/ForeignKeyTestCase.php @@ -1,6 +1,7 @@ assertEqual($album->cover->content, 'lalala'); } + + function test_self_reference_fk() + { + $item = ItemRecord::finder()->withRelated_Items()->findByPk(1); + $this->assertNotNull($item); + $this->assertEqual($item->name, "Professional Work Attire"); + + $this->assertEqual(count($item->related_items),2); + $this->assertEqual($item->related_items[0]->name, "Nametags"); + $this->assertEqual($item->related_items[0]->item_id, 2); + + $this->assertEqual($item->related_items[1]->name, "Grooming and Hygiene"); + $this->assertEqual($item->related_items[1]->item_id, 3); + } } ?> \ No newline at end of file diff --git a/tests/simple_unit/ActiveRecord/ForeignObjectUpdateTest.php b/tests/simple_unit/ActiveRecord/ForeignObjectUpdateTest.php new file mode 100644 index 00000000..77b9cdf3 --- /dev/null +++ b/tests/simple_unit/ActiveRecord/ForeignObjectUpdateTest.php @@ -0,0 +1,211 @@ +OnExecuteCommand[] = array($this,'logger'); + } + return $conn; + } + + function logger($sender,$param) + { + } +} + +class TeamRecord extends BaseFkRecord +{ + const TABLE='teams'; + public $name; + public $location; + + public $players=array(); + + //define the $player member having has many relationship with PlayerRecord + protected static $RELATIONS=array + ( + 'players' => array(self::HAS_MANY, 'PlayerRecord'), + ); + + public static function finder($className=__CLASS__) + { + return parent::finder($className); + } +} + +class PlayerRecord extends BaseFkRecord +{ + const TABLE='players'; + public $player_id; + public $age; + public $team_name; + + public $team; + public $skills=array(); + public $profile; + + protected static $RELATIONS=array + ( + 'skills' => array(self::HAS_MANY, 'SkillRecord', 'player_skills'), + 'team' => array(self::BELONGS_TO, 'TeamRecord'), + 'profile' => array(self::HAS_ONE, 'ProfileRecord'), + ); + + public static function finder($className=__CLASS__) + { + return parent::finder($className); + } +} + +class ProfileRecord extends BaseFkRecord +{ + const TABLE='profiles'; + public $fk_player_id; + public $salary; + + public $player; + + protected static $RELATIONS=array + ( + 'player' => array(self::BELONGS_TO, 'PlayerRecord'), + ); + + public static function finder($className=__CLASS__) + { + return parent::finder($className); + } +} + +class SkillRecord extends BaseFkRecord +{ + const TABLE='skills'; + public $skill_id; + public $name; + + public $players=array(); + + protected static $RELATIONS=array + ( + 'players' => array(self::HAS_MANY, 'PlayerRecord', 'player_skills'), + ); + + public static function finder($className=__CLASS__) + { + return parent::finder($className); + } +} + +class ForeignObjectUpdateTest extends UnitTestCase +{ + function test_add_has_one() + { + ProfileRecord::finder()->deleteByPk(3); + + $player = PlayerRecord::finder()->findByPk(3); + $player->profile = new ProfileRecord(array('salary'=>50000)); + $player->save(); + + //test insert + $player2 = PlayerRecord::finder()->withProfile()->findByPk(3); + $this->assertEqual($player2->profile->salary,50000); + + $player2->profile->salary = 45000; + $player2->save(); + $this->assertEqual($player2->profile->salary,45000); + + //test update + $player3 = PlayerRecord::finder()->withProfile()->findByPk(3); + $this->assertEqual($player3->profile->salary,45000); + } + + function test_add_many() + { + PlayerRecord::finder()->deleteAll("player_id > ?", 3); + + $team = TeamRecord::finder()->findByPk('Team b'); + $team->players[] = new PlayerRecord(array('age'=>20)); + $team->players[] = new PlayerRecord(array('age'=>25)); + $team->save(); + + //test insert + $team1 = TeamRecord::finder()->withPlayers()->findByPk('Team b'); + $this->assertEqual(count($team1->players),3); + $this->assertEqual($team1->players[0]->age, 18); + $this->assertEqual($team1->players[1]->age, 20); + $this->assertEqual($team1->players[2]->age, 25); + + //test update + $team1->players[1]->age = 55; + $team1->save(); + + $this->assertEqual($team1->players[0]->age, 18); + $this->assertEqual($team1->players[1]->age, 55); + $this->assertEqual($team1->players[2]->age, 25); + + $criteria = new TActiveRecordCriteria(); + $criteria->OrdersBy['age'] = 'desc'; + $team2 = TeamRecord::finder()->withPlayers($criteria)->findByPk('Team b'); + $this->assertEqual(count($team2->players),3); + //ordered by age + $this->assertEqual($team2->players[0]->age, 55); + $this->assertEqual($team2->players[1]->age, 25); + $this->assertEqual($team2->players[2]->age, 18); + } + + function test_add_belongs_to() + { + TeamRecord::finder()->deleteByPk('Team c'); + PlayerRecord::finder()->deleteAll("player_id > ?", 3); + + $player = new PlayerRecord(array('age'=>27)); + $player->team = new TeamRecord(array('name'=>'Team c', 'location'=>'Sydney')); + $player->save(); + + //test insert + $player1 = PlayerRecord::finder()->withTeam()->findByAge(27); + $this->assertNotNull($player1); + $this->assertNotNull($player1->team); + $this->assertEqual($player1->team->name, 'Team c'); + $this->assertEqual($player1->team->location, 'Sydney'); + } + + function test_add_many_via_association() + { + PlayerRecord::finder()->deleteAll("player_id > ?", 3); + SkillRecord::finder()->deleteAll("skill_id > ?", 3); + + $player = new PlayerRecord(array('age'=>37)); + $player->skills[] = new SkillRecord(array('name'=>'Bash')); + $player->skills[] = new SkillRecord(array('name'=>'Jump')); + $player->save(); + + //test insert + $player2 = PlayerRecord::finder()->withSkills()->findByAge(37); + $this->assertNotNull($player2); + $this->assertEqual(count($player2->skills), 2); + $this->assertEqual($player2->skills[0]->name, 'Bash'); + $this->assertEqual($player2->skills[1]->name, 'Jump'); + + //test update + $player2->skills[1]->name = "Skip"; + $player2->skills[] = new SkillRecord(array('name'=>'Push')); + $player2->save(); + + $criteria = new TActiveRecordCriteria(); + $criteria->OrdersBy['name'] = 'asc'; + $player3 = PlayerRecord::finder()->withSkills($criteria)->findByAge(37); + $this->assertNotNull($player3); + $this->assertEqual(count($player3->skills), 3); + $this->assertEqual($player3->skills[0]->name, 'Bash'); + $this->assertEqual($player3->skills[1]->name, 'Push'); + $this->assertEqual($player3->skills[2]->name, 'Skip'); + } +} + +?> \ No newline at end of file diff --git a/tests/simple_unit/ActiveRecord/records/ItemRecord.php b/tests/simple_unit/ActiveRecord/records/ItemRecord.php new file mode 100644 index 00000000..189d22c9 --- /dev/null +++ b/tests/simple_unit/ActiveRecord/records/ItemRecord.php @@ -0,0 +1,49 @@ + array(self::HAS_MANY, 'ItemRecord', 'related_items.(related_item_id)'), + ); + + public function getDbConnection() + { + static $conn; + if($conn===null) + { + $conn = new TDbConnection('pgsql:host=localhost;dbname=test', 'test','test'); + $this->OnExecuteCommand[] = array($this,'logger'); + } + return $conn; + } + + public function logger($sender,$param) + { + //var_dump($param->Command->Text); + } + + public static function finder($className=__CLASS__) + { + return parent::finder($className); + } +} + +?> \ No newline at end of file diff --git a/tests/simple_unit/SqlMap/ActiveRecordSqlMapTest.php b/tests/simple_unit/SqlMap/ActiveRecordSqlMapTest.php index 81ce93bb..ebc3ad46 100644 --- a/tests/simple_unit/SqlMap/ActiveRecordSqlMapTest.php +++ b/tests/simple_unit/SqlMap/ActiveRecordSqlMapTest.php @@ -40,7 +40,7 @@ class ActiveRecordSqlMapTest extends BaseCase foreach($records as $record) { $this->assertEqual(get_class($record), 'ActiveAccount'); - $this->assertTrue($registry->getIsCleanObject($record)); + $this->assertTrue($registry->isCleanObject($record)); } } @@ -51,7 +51,7 @@ class ActiveRecordSqlMapTest extends BaseCase foreach($records as $record) { $this->assertEqual(get_class($record), 'ActiveAccount'); - $this->assertTrue($registry->getIsCleanObject($record)); + $this->assertTrue($registry->isCleanObject($record)); } } @@ -60,7 +60,7 @@ class ActiveRecordSqlMapTest extends BaseCase $record = $this->sqlmap->queryForObject('GetActiveRecordAccounts'); $registry=TActiveRecordManager::getInstance()->getObjectStateRegistry(); $record->Account_FirstName = "Testing 123"; - $this->assertTrue($registry->getIsDirtyObject($record)); + $this->assertTrue($registry->isDirtyObject($record)); $this->assertTrue($record->save()); -- cgit v1.2.3