From 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 Mon Sep 17 00:00:00 2001 From: ctrlaltca <> Date: Thu, 12 Jul 2012 11:21:01 +0000 Subject: standardize the use of unix eol; use svn properties to enforce native eol --- .../Relations/TActiveRecordBelongsTo.php | 274 ++++---- .../Relations/TActiveRecordHasMany.php | 240 +++---- .../Relations/TActiveRecordHasManyAssociation.php | 750 ++++++++++----------- .../ActiveRecord/Relations/TActiveRecordHasOne.php | 288 ++++---- .../Relations/TActiveRecordRelation.php | 508 +++++++------- .../Relations/TActiveRecordRelationContext.php | 458 ++++++------- 6 files changed, 1259 insertions(+), 1259 deletions(-) (limited to 'framework/Data/ActiveRecord/Relations') diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php index 68510042..f89660a6 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php @@ -1,138 +1,138 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - */ - -/** - * Loads base active record relationship class. - */ -Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); - -/** - * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between - * the source objects and the related foreign object. Consider the - * entity relationship between a Team and a Player. - * - * +------+ +--------+ - * | Team | 1 <----- * | Player | - * +------+ +--------+ - * - * Where one team may have 0 or more players and each player belongs to only - * one team. We may model Team-Player object relationship as active record as follows. - * - * class TeamRecord extends TActiveRecord - * { - * // see TActiveRecordHasMany for detailed definition. - * } - * class PlayerRecord extends TActiveRecord - * { - * const TABLE='player'; - * public $player_id; //primary key - * public $team_name; //foreign key player.team_name <-> team.name - * public $age; - * public $team; //foreign object TeamRecord - * - * public static $RELATIONS = array - * ( - * 'team' => array(self::BELONGS_TO, 'TeamRecord') - * ); - * - * public static function finder($className=__CLASS__) - * { - * return parent::finder($className); - * } - * } - * - * The static $RELATIONS property of PlayerRecord defines that the - * property $team belongs to a TeamRecord. - * - * The team object may be fetched as follows. - * - * $players = PlayerRecord::finder()->with_team()->findAll(); - * - * The method with_xxx() (where xxx is the relationship property - * name, in this case, team) fetchs the corresponding TeamRecords using - * a second query (not by using a join). The with_xxx() accepts the same - * arguments as other finder methods of TActiveRecord, e.g. - * with_team('location = ?', 'Madrid'). - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - * @since 3.1 - */ -class TActiveRecordBelongsTo extends TActiveRecordRelation -{ - /** - * Get the foreign key index values from the results and make calls to the - * database to find the corresponding foreign objects. - * @param array original results. - */ - protected function collectForeignObjects(&$results) - { - $fkeys = $this->getRelationForeignKeys(); - - $properties = array_keys($fkeys); - $fields = array_values($fkeys); - $indexValues = $this->getIndexValues($properties, $results); - $fkObjects = $this->findForeignObjects($fields, $indexValues); - $this->populateResult($results,$properties,$fkObjects,$fields); - } - - /** - * @return array foreign key field names as key and object properties as value. - * @since 3.1.2 - */ - public function getRelationForeignKeys() - { - $fkObject = $this->getContext()->getForeignRecordFinder(); - return $this->findForeignKeys($this->getSourceRecord(),$fkObject); - } - - /** - * Sets the foreign objects to the given property on the source object. - * @param TActiveRecord source object. - * @param array foreign objects. - */ - protected function setObjectProperty($source, $properties, &$collections) - { - $hash = $this->getObjectHash($source, $properties); - $prop = $this->getContext()->getProperty(); - if(isset($collections[$hash]) && count($collections[$hash]) > 0) - { - if(count($collections[$hash]) > 1) - throw new TActiveRecordException('ar_belongs_to_multiple_result'); - $source->$prop=$collections[$hash][0]; - } - else - $source->$prop=null; - } - - /** - * 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->getColumnValue($this->getContext()->getProperty()); - if($fkObject!==null) - { - $fkObject->save(); - $source = $this->getSourceRecord(); - $fkeys = $this->findForeignKeys($source, $fkObject); - foreach($fkeys as $srcKey => $fKey) - $source->setColumnValue($srcKey, $fkObject->getColumnValue($fKey)); - return true; - } - return false; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relationship class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between + * the source objects and the related foreign object. Consider the + * entity relationship between a Team and a Player. + * + * +------+ +--------+ + * | Team | 1 <----- * | Player | + * +------+ +--------+ + * + * Where one team may have 0 or more players and each player belongs to only + * one team. We may model Team-Player object relationship as active record as follows. + * + * class TeamRecord extends TActiveRecord + * { + * // see TActiveRecordHasMany for detailed definition. + * } + * class PlayerRecord extends TActiveRecord + * { + * const TABLE='player'; + * public $player_id; //primary key + * public $team_name; //foreign key player.team_name <-> team.name + * public $age; + * public $team; //foreign object TeamRecord + * + * public static $RELATIONS = array + * ( + * 'team' => array(self::BELONGS_TO, 'TeamRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * The static $RELATIONS property of PlayerRecord defines that the + * property $team belongs to a TeamRecord. + * + * The team object may be fetched as follows. + * + * $players = PlayerRecord::finder()->with_team()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, team) fetchs the corresponding TeamRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord, e.g. + * with_team('location = ?', 'Madrid'). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordBelongsTo extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + + $properties = array_keys($fkeys); + $fields = array_values($fkeys); + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields, $indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($this->getSourceRecord(),$fkObject); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + if(isset($collections[$hash]) && count($collections[$hash]) > 0) + { + if(count($collections[$hash]) > 1) + throw new TActiveRecordException('ar_belongs_to_multiple_result'); + $source->$prop=$collections[$hash][0]; + } + else + $source->$prop=null; + } + + /** + * 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->getColumnValue($this->getContext()->getProperty()); + if($fkObject!==null) + { + $fkObject->save(); + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($source, $fkObject); + foreach($fkeys as $srcKey => $fKey) + $source->setColumnValue($srcKey, $fkObject->getColumnValue($fKey)); + return true; + } + return false; + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php index f5bc4438..6c5628b8 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php @@ -1,121 +1,121 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - */ - -/** - * Loads base active record relations class. - */ -Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); - -/** - * Implements TActiveRecord::HAS_MANY relationship between the source object having zero or - * more foreign objects. Consider the entity relationship between a Team and a Player. - * - * +------+ +--------+ - * | Team | 1 <----- * | Player | - * +------+ +--------+ - * - * Where one team may have 0 or more players and each player belongs to only - * one team. We may model Team-Player object relationship as active record as follows. - * - * class TeamRecord extends TActiveRecord - * { - * const TABLE='team'; - * public $name; //primary key - * public $location; - * - * public $players=array(); //list of players - * - * public static $RELATIONS=array - * ( - * 'players' => array(self::HAS_MANY, 'PlayerRecord') - * ); - * - * public static function finder($className=__CLASS__) - * { - * return parent::finder($className); - * } - * } - * class PlayerRecord extends TActiveRecord - * { - * // see TActiveRecordBelongsTo for detailed definition - * } - * - * The static $RELATIONS property of TeamRecord defines that the - * property $players has many PlayerRecords. - * - * The players list may be fetched as follows. - * - * $team = TeamRecord::finder()->with_players()->findAll(); - * - * The method with_xxx() (where xxx is the relationship property - * name, in this case, players) fetchs the corresponding PlayerRecords using - * a second query (not by using a join). The with_xxx() accepts the same - * arguments as other finder methods of TActiveRecord, e.g. with_players('age < ?', 35). - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - * @since 3.1 - */ -class TActiveRecordHasMany extends TActiveRecordRelation -{ - /** - * Get the foreign key index values from the results and make calls to the - * database to find the corresponding foreign objects. - * @param array original results. - */ - protected function collectForeignObjects(&$results) - { - $fkeys = $this->getRelationForeignKeys(); - - $properties = array_values($fkeys); - $fields = array_keys($fkeys); - - $indexValues = $this->getIndexValues($properties, $results); - $fkObjects = $this->findForeignObjects($fields,$indexValues); - $this->populateResult($results,$properties,$fkObjects,$fields); - } - - /** - * @return array foreign key field names as key and object properties as value. - * @since 3.1.2 - */ - public function getRelationForeignKeys() - { - $fkObject = $this->getContext()->getForeignRecordFinder(); - return $this->findForeignKeys($fkObject, $this->getSourceRecord()); - } - - /** - * 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(); - $fkeys = $this->findForeignKeys($fkObjects[0], $source); - for($i=0;$i<$total;$i++) - { - foreach($fkeys as $fKey => $srcKey) - $fkObjects[$i]->setColumnValue($fKey, $source->getColumnValue($srcKey)); - $success = $fkObjects[$i]->save() && $success; - } - } - return $success; - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relations class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements TActiveRecord::HAS_MANY relationship between the source object having zero or + * more foreign objects. Consider the entity relationship between a Team and a Player. + * + * +------+ +--------+ + * | Team | 1 <----- * | Player | + * +------+ +--------+ + * + * Where one team may have 0 or more players and each player belongs to only + * one team. We may model Team-Player object relationship as active record as follows. + * + * class TeamRecord extends TActiveRecord + * { + * const TABLE='team'; + * public $name; //primary key + * public $location; + * + * public $players=array(); //list of players + * + * public static $RELATIONS=array + * ( + * 'players' => array(self::HAS_MANY, 'PlayerRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class PlayerRecord extends TActiveRecord + * { + * // see TActiveRecordBelongsTo for detailed definition + * } + * + * The static $RELATIONS property of TeamRecord defines that the + * property $players has many PlayerRecords. + * + * The players list may be fetched as follows. + * + * $team = TeamRecord::finder()->with_players()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, players) fetchs the corresponding PlayerRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord, e.g. with_players('age < ?', 35). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasMany extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + + $properties = array_values($fkeys); + $fields = array_keys($fkeys); + + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields,$indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($fkObject, $this->getSourceRecord()); + } + + /** + * 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(); + $fkeys = $this->findForeignKeys($fkObjects[0], $source); + for($i=0;$i<$total;$i++) + { + foreach($fkeys as $fKey => $srcKey) + $fkObjects[$i]->setColumnValue($fKey, $source->getColumnValue($srcKey)); + $success = $fkObjects[$i]->save() && $success; + } + } + return $success; + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php index fe368f9c..28ebb317 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php @@ -1,376 +1,376 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - */ - -/** - * Loads base active record relations class. - */ -Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); - -/** - * Implements the M-N (many to many) relationship via association table. - * Consider the entity relationship between Articles and Categories - * via the association table Article_Category. - * - * +---------+ +------------------+ +----------+ - * | Article | * -----> * | Article_Category | * <----- * | Category | - * +---------+ +------------------+ +----------+ - * - * Where one article may have 0 or more categories and each category may have 0 - * or more articles. We may model Article-Category object relationship - * as active record as follows. - * - * class ArticleRecord - * { - * const TABLE='Article'; - * public $article_id; - * - * public $Categories=array(); //foreign object collection. - * - * public static $RELATIONS = array - * ( - * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category') - * ); - * - * public static function finder($className=__CLASS__) - * { - * return parent::finder($className); - * } - * } - * class CategoryRecord - * { - * const TABLE='Category'; - * public $category_id; - * - * public $Articles=array(); - * - * public static $RELATIONS = array - * ( - * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category') - * ); - * - * public static function finder($className=__CLASS__) - * { - * return parent::finder($className); - * } - * } - * - * - * The static $RELATIONS property of ArticleRecord defines that the - * property $Categories has many CategoryRecords. Similar, the - * static $RELATIONS property of CategoryRecord defines many ArticleRecords. - * - * The articles with categories list may be fetched as follows. - * - * $articles = TeamRecord::finder()->withCategories()->findAll(); - * - * The method with_xxx() (where xxx is the relationship property - * name, in this case, Categories) fetchs the corresponding CategoryRecords using - * a second query (not by using a join). The with_xxx() accepts the same - * arguments as other finder methods of TActiveRecord. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - * @since 3.1 - */ -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 - * database to find the corresponding foreign objects using association table. - * @param array original results. - */ - protected function collectForeignObjects(&$results) - { - list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys(); - $properties = array_values($sourceKeys); - $indexValues = $this->getIndexValues($properties, $results); - $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys); - } - - /** - * @return array 2 arrays of source keys and foreign keys from the association table. - */ - public function getRelationForeignKeys() - { - $association = $this->getAssociationTable(); - $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true); - $fkObject = $this->getContext()->getForeignRecordFinder(); - $foreignKeys = $this->findForeignKeys($association, $fkObject); - return array($sourceKeys, $foreignKeys); - } - - /** - * @return TDbTableInfo association table information. - */ - protected function getAssociationTable() - { - if($this->_association===null) - { - $gateway = $this->getSourceRecord()->getRecordGateway(); - $conn = $this->getSourceRecord()->getDbConnection(); - //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; - } - - /** - * @return TDbTableInfo source table information. - */ - protected function getSourceTable() - { - if($this->_sourceTable===null) - { - $gateway = $this->getSourceRecord()->getRecordGateway(); - $this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord()); - } - return $this->_sourceTable; - } - - /** - * @return TDbTableInfo foreign table information. - */ - protected function getForeignTable() - { - if($this->_foreignTable===null) - { - $gateway = $this->getSourceRecord()->getRecordGateway(); - $fkObject = $this->getContext()->getForeignRecordFinder(); - $this->_foreignTable = $gateway->getRecordTableInfo($fkObject); - } - return $this->_foreignTable; - } - - /** - * @return TDataGatewayCommand - */ - protected function getCommandBuilder() - { - return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord()); - } - - /** - * @return TDataGatewayCommand - */ - protected function getForeignCommandBuilder() - { - $obj = $this->getContext()->getForeignRecordFinder(); - return $this->getSourceRecord()->getRecordGateway()->getCommand($obj); - } - - - /** - * Fetches the foreign objects using TActiveRecord::findAllByIndex() - * @param array field names - * @param array foreign key index values. - */ - protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys) - { - $criteria = $this->getCriteria(); - $finder = $this->getContext()->getForeignRecordFinder(); - $type = get_class($finder); - $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys); - $srcProps = array_keys($sourceKeys); - $collections=array(); - foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row) - { - $hash = $this->getObjectHash($row, $srcProps); - foreach($srcProps as $column) - unset($row[$column]); - $obj = $this->createFkObject($type,$row,$foreignKeys); - $collections[$hash][] = $obj; - } - $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 = TActiveRecord::createRecord($type, $row); - if(count($this->_association_columns) > 0) - { - $i=0; - foreach($foreignKeys as $ref=>$fk) - $obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]); - } - return $obj; - } - - /** - * @param TSqlCriteria - * @param TTableInfo association table info - * @param array field names - * @param array field values - */ - public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys) - { - $innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys); - $fkTable = $this->getForeignTable()->getTableFullName(); - $srcColumns = $this->getSourceColumns($sourceKeys); - if(($where=$criteria->getCondition())===null) - $where='1=1'; - $sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}"; - - $parameters = $criteria->getParameters()->toArray(); - $ordering = $criteria->getOrdersBy(); - $limit = $criteria->getLimit(); - $offset = $criteria->getOffset(); - - $builder = $this->getForeignCommandBuilder()->getBuilder(); - $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset); - $this->getCommandBuilder()->onCreateCommand($command, $criteria); - return $command; - } - - /** - * @param array source table column names. - * @return string comma separated source column names. - */ - protected function getSourceColumns($sourceKeys) - { - $columns=array(); - $table = $this->getAssociationTable(); - $tableName = $table->getTableFullName(); - $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns); - foreach($columnNames as $name) - $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName(); - return implode(', ', $columns); - } - - /** - * SQL inner join for M-N relationship via association table. - * @param array foreign table column key names. - * @param array source table index values. - * @param array source table column names. - * @return string inner join condition for M-N relationship via association table. - */ - protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys) - { - $refInfo= $this->getAssociationTable(); - $fkInfo = $this->getForeignTable(); - - $refTable = $refInfo->getTableFullName(); - $fkTable = $fkInfo->getTableFullName(); - - $joins = array(); - $hasAssociationColumns = count($this->_association_columns) > 0; - $i=0; - foreach($foreignKeys as $ref=>$fk) - { - 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}"; - } - $joinCondition = implode(' AND ', $joins); - $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(); - $builder = $this->getAssociationTableCommandBuilder(); - for($i=0;$i<$total;$i++) - $success = $fkObjects[$i]->save() && $success; - return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success; - } - return $success; - } - - /** - * @return TDbCommandBuilder - */ - protected function getAssociationTableCommandBuilder() - { - $conn = $this->getContext()->getSourceRecord()->getDbConnection(); - return $this->getAssociationTable()->createCommandBuilder($conn); - } - - private 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; - } - - private function addAssociationData($builder,$data) - { - $command = $builder->createInsertCommand($data); - return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0; - } - - private 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; - } - - private 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; - } - - private function getForeignObjectValues($foreignKeys,$fkObject) - { - $data=array(); - foreach($foreignKeys as $name=>$fKey) - $data[$name] = $fkObject->getColumnValue($fKey); - return $data; - } -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relations class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * Implements the M-N (many to many) relationship via association table. + * Consider the entity relationship between Articles and Categories + * via the association table Article_Category. + * + * +---------+ +------------------+ +----------+ + * | Article | * -----> * | Article_Category | * <----- * | Category | + * +---------+ +------------------+ +----------+ + * + * Where one article may have 0 or more categories and each category may have 0 + * or more articles. We may model Article-Category object relationship + * as active record as follows. + * + * class ArticleRecord + * { + * const TABLE='Article'; + * public $article_id; + * + * public $Categories=array(); //foreign object collection. + * + * public static $RELATIONS = array + * ( + * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class CategoryRecord + * { + * const TABLE='Category'; + * public $category_id; + * + * public $Articles=array(); + * + * public static $RELATIONS = array + * ( + * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * + * The static $RELATIONS property of ArticleRecord defines that the + * property $Categories has many CategoryRecords. Similar, the + * static $RELATIONS property of CategoryRecord defines many ArticleRecords. + * + * The articles with categories list may be fetched as follows. + * + * $articles = TeamRecord::finder()->withCategories()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, Categories) fetchs the corresponding CategoryRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +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 + * database to find the corresponding foreign objects using association table. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys(); + $properties = array_values($sourceKeys); + $indexValues = $this->getIndexValues($properties, $results); + $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys); + } + + /** + * @return array 2 arrays of source keys and foreign keys from the association table. + */ + public function getRelationForeignKeys() + { + $association = $this->getAssociationTable(); + $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true); + $fkObject = $this->getContext()->getForeignRecordFinder(); + $foreignKeys = $this->findForeignKeys($association, $fkObject); + return array($sourceKeys, $foreignKeys); + } + + /** + * @return TDbTableInfo association table information. + */ + protected function getAssociationTable() + { + if($this->_association===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $conn = $this->getSourceRecord()->getDbConnection(); + //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; + } + + /** + * @return TDbTableInfo source table information. + */ + protected function getSourceTable() + { + if($this->_sourceTable===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord()); + } + return $this->_sourceTable; + } + + /** + * @return TDbTableInfo foreign table information. + */ + protected function getForeignTable() + { + if($this->_foreignTable===null) + { + $gateway = $this->getSourceRecord()->getRecordGateway(); + $fkObject = $this->getContext()->getForeignRecordFinder(); + $this->_foreignTable = $gateway->getRecordTableInfo($fkObject); + } + return $this->_foreignTable; + } + + /** + * @return TDataGatewayCommand + */ + protected function getCommandBuilder() + { + return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord()); + } + + /** + * @return TDataGatewayCommand + */ + protected function getForeignCommandBuilder() + { + $obj = $this->getContext()->getForeignRecordFinder(); + return $this->getSourceRecord()->getRecordGateway()->getCommand($obj); + } + + + /** + * Fetches the foreign objects using TActiveRecord::findAllByIndex() + * @param array field names + * @param array foreign key index values. + */ + protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys) + { + $criteria = $this->getCriteria(); + $finder = $this->getContext()->getForeignRecordFinder(); + $type = get_class($finder); + $command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys); + $srcProps = array_keys($sourceKeys); + $collections=array(); + foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row) + { + $hash = $this->getObjectHash($row, $srcProps); + foreach($srcProps as $column) + unset($row[$column]); + $obj = $this->createFkObject($type,$row,$foreignKeys); + $collections[$hash][] = $obj; + } + $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 = TActiveRecord::createRecord($type, $row); + if(count($this->_association_columns) > 0) + { + $i=0; + foreach($foreignKeys as $ref=>$fk) + $obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]); + } + return $obj; + } + + /** + * @param TSqlCriteria + * @param TTableInfo association table info + * @param array field names + * @param array field values + */ + public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys) + { + $innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys); + $fkTable = $this->getForeignTable()->getTableFullName(); + $srcColumns = $this->getSourceColumns($sourceKeys); + if(($where=$criteria->getCondition())===null) + $where='1=1'; + $sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}"; + + $parameters = $criteria->getParameters()->toArray(); + $ordering = $criteria->getOrdersBy(); + $limit = $criteria->getLimit(); + $offset = $criteria->getOffset(); + + $builder = $this->getForeignCommandBuilder()->getBuilder(); + $command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset); + $this->getCommandBuilder()->onCreateCommand($command, $criteria); + return $command; + } + + /** + * @param array source table column names. + * @return string comma separated source column names. + */ + protected function getSourceColumns($sourceKeys) + { + $columns=array(); + $table = $this->getAssociationTable(); + $tableName = $table->getTableFullName(); + $columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns); + foreach($columnNames as $name) + $columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName(); + return implode(', ', $columns); + } + + /** + * SQL inner join for M-N relationship via association table. + * @param array foreign table column key names. + * @param array source table index values. + * @param array source table column names. + * @return string inner join condition for M-N relationship via association table. + */ + protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys) + { + $refInfo= $this->getAssociationTable(); + $fkInfo = $this->getForeignTable(); + + $refTable = $refInfo->getTableFullName(); + $fkTable = $fkInfo->getTableFullName(); + + $joins = array(); + $hasAssociationColumns = count($this->_association_columns) > 0; + $i=0; + foreach($foreignKeys as $ref=>$fk) + { + 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}"; + } + $joinCondition = implode(' AND ', $joins); + $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(); + $builder = $this->getAssociationTableCommandBuilder(); + for($i=0;$i<$total;$i++) + $success = $fkObjects[$i]->save() && $success; + return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success; + } + return $success; + } + + /** + * @return TDbCommandBuilder + */ + protected function getAssociationTableCommandBuilder() + { + $conn = $this->getContext()->getSourceRecord()->getDbConnection(); + return $this->getAssociationTable()->createCommandBuilder($conn); + } + + private 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; + } + + private function addAssociationData($builder,$data) + { + $command = $builder->createInsertCommand($data); + return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0; + } + + private 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; + } + + private 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; + } + + private function getForeignObjectValues($foreignKeys,$fkObject) + { + $data=array(); + foreach($foreignKeys as $name=>$fKey) + $data[$name] = $fkObject->getColumnValue($fKey); + return $data; + } +} diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php index bc895901..a762e196 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php @@ -1,145 +1,145 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - */ - -/** - * Loads base active record relationship class. - */ -Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); - -/** - * TActiveRecordHasOne models the object relationship that a record (the source object) - * property is an instance of foreign record object having a foreign key - * related to the source object. The HAS_ONE relation is very similar to the - * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view). - * - * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular. - * That is, HAS_MANY will return a collection of records while HAS_ONE returns the - * corresponding record. - * - * Consider the entity relationship between a Car and a Engine. - * - * +-----+ +--------+ - * | Car | 1 <----- 1 | Engine | - * +-----+ +--------+ - * - * Where each engine belongs to only one car, that is, the Engine entity has - * a foreign key to the Car's primary key. We may model - * Engine-Car object relationship as active record as follows. - * - * class CarRecord extends TActiveRecord - * { - * const TABLE='car'; - * public $car_id; //primary key - * public $colour; - * - * public $engine; //engine foreign object - * - * public static $RELATIONS=array - * ( - * 'engine' => array(self::HAS_ONE, 'EngineRecord') - * ); - * - * public static function finder($className=__CLASS__) - * { - * return parent::finder($className); - * } - * } - * class EngineRecord extends TActiveRecord - * { - * const TABLE='engine'; - * public $engine_id; - * public $capacity; - * public $car_id; //foreign key to cars - * - * public static function finder($className=__CLASS__) - * { - * return parent::finder($className); - * } - * } - * - * The static $RELATIONS property of CarRecord defines that the - * property $engine that will reference an EngineRecord instance. - * - * The car record with engine property list may be fetched as follows. - * - * $cars = CarRecord::finder()->with_engine()->findAll(); - * - * The method with_xxx() (where xxx is the relationship property - * name, in this case, engine) fetchs the corresponding EngineRecords using - * a second query (not by using a join). The with_xxx() accepts the same - * arguments as other finder methods of TActiveRecord, e.g. with_engine('capacity < ?', 3.8). - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - * @since 3.1 - */ -class TActiveRecordHasOne extends TActiveRecordRelation -{ - /** - * Get the foreign key index values from the results and make calls to the - * database to find the corresponding foreign objects. - * @param array original results. - */ - protected function collectForeignObjects(&$results) - { - $fkeys = $this->getRelationForeignKeys(); - $properties = array_values($fkeys); - $fields = array_keys($fkeys); - - $indexValues = $this->getIndexValues($properties, $results); - $fkObjects = $this->findForeignObjects($fields,$indexValues); - $this->populateResult($results,$properties,$fkObjects,$fields); - } - - /** - * @return array foreign key field names as key and object properties as value. - * @since 3.1.2 - */ - public function getRelationForeignKeys() - { - $fkObject = $this->getContext()->getForeignRecordFinder(); - return $this->findForeignKeys($fkObject, $this->getSourceRecord()); - } - - /** - * Sets the foreign objects to the given property on the source object. - * @param TActiveRecord source object. - * @param array foreign objects. - */ - protected function setObjectProperty($source, $properties, &$collections) - { - $hash = $this->getObjectHash($source, $properties); - $prop = $this->getContext()->getProperty(); - if(isset($collections[$hash]) && count($collections[$hash]) > 0) - { - if(count($collections[$hash]) > 1) - throw new TActiveRecordException('ar_belongs_to_multiple_result'); - $source->setColumnValue($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(); - $source = $this->getSourceRecord(); - $fkeys = $this->findForeignKeys($fkObject, $source); - foreach($fkeys as $fKey => $srcKey) - $fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey)); - return $fkObject->save(); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Loads base active record relationship class. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); + +/** + * TActiveRecordHasOne models the object relationship that a record (the source object) + * property is an instance of foreign record object having a foreign key + * related to the source object. The HAS_ONE relation is very similar to the + * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view). + * + * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular. + * That is, HAS_MANY will return a collection of records while HAS_ONE returns the + * corresponding record. + * + * Consider the entity relationship between a Car and a Engine. + * + * +-----+ +--------+ + * | Car | 1 <----- 1 | Engine | + * +-----+ +--------+ + * + * Where each engine belongs to only one car, that is, the Engine entity has + * a foreign key to the Car's primary key. We may model + * Engine-Car object relationship as active record as follows. + * + * class CarRecord extends TActiveRecord + * { + * const TABLE='car'; + * public $car_id; //primary key + * public $colour; + * + * public $engine; //engine foreign object + * + * public static $RELATIONS=array + * ( + * 'engine' => array(self::HAS_ONE, 'EngineRecord') + * ); + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * class EngineRecord extends TActiveRecord + * { + * const TABLE='engine'; + * public $engine_id; + * public $capacity; + * public $car_id; //foreign key to cars + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * The static $RELATIONS property of CarRecord defines that the + * property $engine that will reference an EngineRecord instance. + * + * The car record with engine property list may be fetched as follows. + * + * $cars = CarRecord::finder()->with_engine()->findAll(); + * + * The method with_xxx() (where xxx is the relationship property + * name, in this case, engine) fetchs the corresponding EngineRecords using + * a second query (not by using a join). The with_xxx() accepts the same + * arguments as other finder methods of TActiveRecord, e.g. with_engine('capacity < ?', 3.8). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordHasOne extends TActiveRecordRelation +{ + /** + * Get the foreign key index values from the results and make calls to the + * database to find the corresponding foreign objects. + * @param array original results. + */ + protected function collectForeignObjects(&$results) + { + $fkeys = $this->getRelationForeignKeys(); + $properties = array_values($fkeys); + $fields = array_keys($fkeys); + + $indexValues = $this->getIndexValues($properties, $results); + $fkObjects = $this->findForeignObjects($fields,$indexValues); + $this->populateResult($results,$properties,$fkObjects,$fields); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + $fkObject = $this->getContext()->getForeignRecordFinder(); + return $this->findForeignKeys($fkObject, $this->getSourceRecord()); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + if(isset($collections[$hash]) && count($collections[$hash]) > 0) + { + if(count($collections[$hash]) > 1) + throw new TActiveRecordException('ar_belongs_to_multiple_result'); + $source->setColumnValue($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(); + $source = $this->getSourceRecord(); + $fkeys = $this->findForeignKeys($fkObject, $source); + foreach($fkeys as $fKey => $srcKey) + $fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey)); + return $fkObject->save(); + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php index 39482635..c59532d9 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php @@ -1,254 +1,254 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - */ - -/** - * Load active record relationship context. - */ -Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext'); - -/** - * Base class for active record relationships. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - * @since 3.1 - */ -abstract class TActiveRecordRelation -{ - private $_context; - private $_criteria; - - public function __construct(TActiveRecordRelationContext $context, $criteria) - { - $this->_context = $context; - $this->_criteria = $criteria; - } - - /** - * @return TActiveRecordRelationContext - */ - protected function getContext() - { - return $this->_context; - } - - /** - * @return TActiveRecordCriteria - */ - protected function getCriteria() - { - return $this->_criteria; - } - - /** - * @return TActiveRecord - */ - protected function getSourceRecord() - { - return $this->getContext()->getSourceRecord(); - } - - abstract protected function collectForeignObjects(&$results); - - /** - * Dispatch the method calls to the source record finder object. When - * an instance of TActiveRecord or an array of TActiveRecord is returned - * the corresponding foreign objects are also fetched and assigned. - * - * Multiple relationship calls can be chain together. - * - * @param string method name called - * @param array method arguments - * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called. - */ - public function __call($method,$args) - { - static $stack=array(); - - $results = call_user_func_array(array($this->getSourceRecord(),$method),$args); - $validArray = is_array($results) && count($results) > 0; - if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord) - { - $this->collectForeignObjects($results); - while($obj = array_pop($stack)) - $obj->collectForeignObjects($results); - } - else if($results instanceof TActiveRecordRelation) - $stack[] = $this; //call it later - else if($results === null || !$validArray) - $stack = array(); - return $results; - } - - /** - * Fetch results for current relationship. - * @return boolean always true. - */ - public function fetchResultsInto($obj) - { - $this->collectForeignObjects($obj); - return true; - } - - /** - * Returns foreign keys in $fromRecord with source column names as key - * and foreign column names in the corresponding $matchesRecord as value. - * The method returns the first matching foreign key between these 2 records. - * @param TActiveRecord $fromRecord - * @param TActiveRecord $matchesRecord - * @return array foreign keys with source column names as key and foreign column names as value. - */ - protected function findForeignKeys($from, $matchesRecord, $loose=false) - { - $gateway = $matchesRecord->getRecordGateway(); - $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord); - $matchingTableName = strtolower($recordTableInfo->getTableName()); - $matchingFullTableName = strtolower($recordTableInfo->getTableFullName()); - $tableInfo=$from; - if($from instanceof TActiveRecord) - $tableInfo = $gateway->getRecordTableInfo($from); - //find first non-empty FK - foreach($tableInfo->getForeignKeys() as $fkeys) - { - $fkTable = strtolower($fkeys['table']); - if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName) - { - $hasFkField = !$loose && $this->getContext()->hasFkField(); - $key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys']; - if(!empty($key)) - return $key; - } - } - - //none found - $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName(); - throw new TActiveRecordException('ar_relations_missing_fk', - $tableInfo->getTableFullName(), $matching); - } - - /** - * @return array foreign key field names as key and object properties as value. - * @since 3.1.2 - */ - abstract public function getRelationForeignKeys(); - - /** - * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION. - * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot - * are ignored. - */ - private function getFkFields($fkeys) - { - $matching = array(); - preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching); - $fields = array(); - foreach($fkeys as $fkName => $field) - { - if(in_array($fkName, $matching[2])) - $fields[$fkName] = $field; - } - return $fields; - } - - /** - * @param mixed object or array to be hashed - * @param array name of property for hashing the properties. - * @return string object hash using crc32 and serialize. - */ - protected function getObjectHash($obj, $properties) - { - $ids=array(); - foreach($properties as $property) - $ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property]; - return serialize($ids); - } - - /** - * Fetches the foreign objects using TActiveRecord::findAllByIndex() - * @param array field names - * @param array foreign key index values. - * @return TActiveRecord[] foreign objects. - */ - protected function findForeignObjects($fields, $indexValues) - { - $finder = $this->getContext()->getForeignRecordFinder(); - return $finder->findAllByIndex($this->_criteria, $fields, $indexValues); - } - - /** - * Obtain the foreign key index values from the results. - * @param array property names - * @param array TActiveRecord results - * @return array foreign key index values. - */ - protected function getIndexValues($keys, $results) - { - if(!is_array($results) && !$results instanceof ArrayAccess) - $results = array($results); - $values=array(); - foreach($results as $result) - { - $value = array(); - foreach($keys as $name) - $value[] = $result->getColumnValue($name); - $values[] = $value; - } - return $values; - } - - /** - * Populate the results with the foreign objects found. - * @param array source results - * @param array source property names - * @param array foreign objects - * @param array foreign object field names. - */ - protected function populateResult(&$results,$properties,&$fkObjects,$fields) - { - $collections=array(); - foreach($fkObjects as $fkObject) - $collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject; - $this->setResultCollection($results, $collections, $properties); - } - - /** - * Populates the result array with foreign objects (matched using foreign key hashed property values). - * @param array $results - * @param array $collections - * @param array property names - */ - protected function setResultCollection(&$results, &$collections, $properties) - { - if(is_array($results) || $results instanceof ArrayAccess) - { - for($i=0,$k=count($results);$i<$k;$i++) - $this->setObjectProperty($results[$i], $properties, $collections); - } - else - $this->setObjectProperty($results, $properties, $collections); - } - - /** - * Sets the foreign objects to the given property on the source object. - * @param TActiveRecord source object. - * @param array source properties - * @param array foreign objects. - */ - protected function setObjectProperty($source, $properties, &$collections) - { - $hash = $this->getObjectHash($source, $properties); - $prop = $this->getContext()->getProperty(); - $source->$prop=isset($collections[$hash]) ? $collections[$hash] : array(); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * Load active record relationship context. + */ +Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext'); + +/** + * Base class for active record relationships. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +abstract class TActiveRecordRelation +{ + private $_context; + private $_criteria; + + public function __construct(TActiveRecordRelationContext $context, $criteria) + { + $this->_context = $context; + $this->_criteria = $criteria; + } + + /** + * @return TActiveRecordRelationContext + */ + protected function getContext() + { + return $this->_context; + } + + /** + * @return TActiveRecordCriteria + */ + protected function getCriteria() + { + return $this->_criteria; + } + + /** + * @return TActiveRecord + */ + protected function getSourceRecord() + { + return $this->getContext()->getSourceRecord(); + } + + abstract protected function collectForeignObjects(&$results); + + /** + * Dispatch the method calls to the source record finder object. When + * an instance of TActiveRecord or an array of TActiveRecord is returned + * the corresponding foreign objects are also fetched and assigned. + * + * Multiple relationship calls can be chain together. + * + * @param string method name called + * @param array method arguments + * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called. + */ + public function __call($method,$args) + { + static $stack=array(); + + $results = call_user_func_array(array($this->getSourceRecord(),$method),$args); + $validArray = is_array($results) && count($results) > 0; + if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord) + { + $this->collectForeignObjects($results); + while($obj = array_pop($stack)) + $obj->collectForeignObjects($results); + } + else if($results instanceof TActiveRecordRelation) + $stack[] = $this; //call it later + else if($results === null || !$validArray) + $stack = array(); + return $results; + } + + /** + * Fetch results for current relationship. + * @return boolean always true. + */ + public function fetchResultsInto($obj) + { + $this->collectForeignObjects($obj); + return true; + } + + /** + * Returns foreign keys in $fromRecord with source column names as key + * and foreign column names in the corresponding $matchesRecord as value. + * The method returns the first matching foreign key between these 2 records. + * @param TActiveRecord $fromRecord + * @param TActiveRecord $matchesRecord + * @return array foreign keys with source column names as key and foreign column names as value. + */ + protected function findForeignKeys($from, $matchesRecord, $loose=false) + { + $gateway = $matchesRecord->getRecordGateway(); + $recordTableInfo = $gateway->getRecordTableInfo($matchesRecord); + $matchingTableName = strtolower($recordTableInfo->getTableName()); + $matchingFullTableName = strtolower($recordTableInfo->getTableFullName()); + $tableInfo=$from; + if($from instanceof TActiveRecord) + $tableInfo = $gateway->getRecordTableInfo($from); + //find first non-empty FK + foreach($tableInfo->getForeignKeys() as $fkeys) + { + $fkTable = strtolower($fkeys['table']); + if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName) + { + $hasFkField = !$loose && $this->getContext()->hasFkField(); + $key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys']; + if(!empty($key)) + return $key; + } + } + + //none found + $matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName(); + throw new TActiveRecordException('ar_relations_missing_fk', + $tableInfo->getTableFullName(), $matching); + } + + /** + * @return array foreign key field names as key and object properties as value. + * @since 3.1.2 + */ + abstract public function getRelationForeignKeys(); + + /** + * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION. + * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot + * are ignored. + */ + private function getFkFields($fkeys) + { + $matching = array(); + preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching); + $fields = array(); + foreach($fkeys as $fkName => $field) + { + if(in_array($fkName, $matching[2])) + $fields[$fkName] = $field; + } + return $fields; + } + + /** + * @param mixed object or array to be hashed + * @param array name of property for hashing the properties. + * @return string object hash using crc32 and serialize. + */ + protected function getObjectHash($obj, $properties) + { + $ids=array(); + foreach($properties as $property) + $ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property]; + return serialize($ids); + } + + /** + * Fetches the foreign objects using TActiveRecord::findAllByIndex() + * @param array field names + * @param array foreign key index values. + * @return TActiveRecord[] foreign objects. + */ + protected function findForeignObjects($fields, $indexValues) + { + $finder = $this->getContext()->getForeignRecordFinder(); + return $finder->findAllByIndex($this->_criteria, $fields, $indexValues); + } + + /** + * Obtain the foreign key index values from the results. + * @param array property names + * @param array TActiveRecord results + * @return array foreign key index values. + */ + protected function getIndexValues($keys, $results) + { + if(!is_array($results) && !$results instanceof ArrayAccess) + $results = array($results); + $values=array(); + foreach($results as $result) + { + $value = array(); + foreach($keys as $name) + $value[] = $result->getColumnValue($name); + $values[] = $value; + } + return $values; + } + + /** + * Populate the results with the foreign objects found. + * @param array source results + * @param array source property names + * @param array foreign objects + * @param array foreign object field names. + */ + protected function populateResult(&$results,$properties,&$fkObjects,$fields) + { + $collections=array(); + foreach($fkObjects as $fkObject) + $collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject; + $this->setResultCollection($results, $collections, $properties); + } + + /** + * Populates the result array with foreign objects (matched using foreign key hashed property values). + * @param array $results + * @param array $collections + * @param array property names + */ + protected function setResultCollection(&$results, &$collections, $properties) + { + if(is_array($results) || $results instanceof ArrayAccess) + { + for($i=0,$k=count($results);$i<$k;$i++) + $this->setObjectProperty($results[$i], $properties, $collections); + } + else + $this->setObjectProperty($results, $properties, $collections); + } + + /** + * Sets the foreign objects to the given property on the source object. + * @param TActiveRecord source object. + * @param array source properties + * @param array foreign objects. + */ + protected function setObjectProperty($source, $properties, &$collections) + { + $hash = $this->getObjectHash($source, $properties); + $prop = $this->getContext()->getProperty(); + $source->$prop=isset($collections[$hash]) ? $collections[$hash] : array(); + } +} + diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php index 8826bb69..dbcc097b 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php @@ -1,230 +1,230 @@ - - * @link http://www.pradosoft.com/ + + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - */ - -/** - * TActiveRecordRelationContext holds information regarding record relationships - * such as record relation property name, query criteria and foreign object record - * class names. - * - * This class is use internally by passing a context to the TActiveRecordRelation - * constructor. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Relations - * @since 3.1 - */ -class TActiveRecordRelationContext -{ - private $_property; - private $_record; - private $_relation; //data from an entry of TActiveRecord::$RELATION - private $_fkeys; - - public function __construct($record, $property=null, $relation=null) - { - $this->_record=$record; - $this->_property=$property; - $this->_relation=$relation; - } - - /** - * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS - * @since 3.1.2 - */ - public function hasRecordRelation() - { - return $this->_relation!==null; - } - - public function getPropertyValue() - { - $obj = $this->getSourceRecord(); - return $obj->getColumnValue($this->getProperty()); - } - - /** - * @return string name of the record property that the relationship results will be assigned to. - */ - public function getProperty() - { - return $this->_property; - } - - /** - * @return TActiveRecord the active record instance that queried for its related records. - */ - public function getSourceRecord() - { - return $this->_record; - } - - /** - * @return array foreign key of this relations, the keys is dependent on the - * relationship type. - * @since 3.1.2 - */ - public function getRelationForeignKeys() - { - if($this->_fkeys===null) - $this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys(); - return $this->_fkeys; - } - - /** - * @return string HAS_MANY, HAS_ONE, or BELONGS_TO - */ - public function getRelationType() - { - return $this->_relation[0]; - } - - /** - * @return string foreign record class name. - */ - public function getForeignRecordClass() - { - return $this->_relation[1]; - } - - /** - * @return string foreign key field names, comma delimited. - * @since 3.1.2 - */ - public function getFkField() - { - return $this->_relation[2]; - } - - /** - * @return string the query condition for the relation as specified in RELATIONS - * @since 3.1.2 - */ - public function getCondition() - { - return isset($this->_relation[3])?$this->_relation[3]:null; - } - - /** - * @return array the query parameters for the relation as specified in RELATIONS - * @since 3.1.2 - */ - public function getParameters() - { - return isset($this->_relation[4])?$this->_relation[4]:array(); - } - - /** - * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set. - * @since 3.1.2 - */ - public function hasFkField() - { - $notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY; - return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); - } - - /** - * @return string the M-N relationship association table name. - */ - public function getAssociationTable() - { - return $this->_relation[2]; - } - - /** - * @return boolean true if the relationship is HAS_MANY and requires an association table. - */ - public function hasAssociationTable() - { - $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY; - return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); - } - - /** - * @return TActiveRecord corresponding relationship foreign object finder instance. - */ - public function getForeignRecordFinder() - { - return TActiveRecord::finder($this->getForeignRecordClass()); - } - - /** - * Creates and return the TActiveRecordRelation handler for specific relationships. - * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany, - * or TActiveRecordHasManyAssocation will be returned. - * @param TActiveRecordCriteria search criteria - * @return TActiveRecordRelation record relationship handler instnace. - * @throws TActiveRecordException if property is not defined or missing. - */ - public function getRelationHandler($criteria=null) - { - if(!$this->hasRecordRelation()) - { - throw new TActiveRecordException('ar_undefined_relation_prop', - $this->_property, get_class($this->_record), 'RELATIONS'); - } - if($criteria===null) - $criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters()); - switch($this->getRelationType()) - { - case TActiveRecord::HAS_MANY: - Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany'); - return new TActiveRecordHasMany($this, $criteria); - case TActiveRecord::MANY_TO_MANY: - Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation'); - return new TActiveRecordHasManyAssociation($this, $criteria); - case TActiveRecord::HAS_ONE: - Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne'); - return new TActiveRecordHasOne($this, $criteria); - case TActiveRecord::BELONGS_TO: - Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo'); - return new TActiveRecordBelongsTo($this, $criteria); - default: - throw new TActiveRecordException('ar_invalid_relationship'); - } - } - - /** - * @return TActiveRecordRelationCommand - */ - public function updateAssociatedRecords($updateBelongsTo=false) - { - $success=true; - foreach($this->_record->getRecordRelations() as $data) - { - list($property, $relation) = $data; - $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO; - if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo)) - { - $obj = $this->getSourceRecord(); - if(!$this->isEmptyFkObject($obj->getColumnValue($property))) - { - $context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation); - $success = $context->getRelationHandler()->updateAssociatedRecords() && $success; - } - } - } - return $success; - } - - protected function isEmptyFkObject($obj) - { - if(is_object($obj)) - return $obj instanceof TList ? $obj->count() === 0 : false; - else if(is_array($obj)) - return count($obj)===0; - else - return empty($obj); - } -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + */ + +/** + * TActiveRecordRelationContext holds information regarding record relationships + * such as record relation property name, query criteria and foreign object record + * class names. + * + * This class is use internally by passing a context to the TActiveRecordRelation + * constructor. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Relations + * @since 3.1 + */ +class TActiveRecordRelationContext +{ + private $_property; + private $_record; + private $_relation; //data from an entry of TActiveRecord::$RELATION + private $_fkeys; + + public function __construct($record, $property=null, $relation=null) + { + $this->_record=$record; + $this->_property=$property; + $this->_relation=$relation; + } + + /** + * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS + * @since 3.1.2 + */ + public function hasRecordRelation() + { + return $this->_relation!==null; + } + + public function getPropertyValue() + { + $obj = $this->getSourceRecord(); + return $obj->getColumnValue($this->getProperty()); + } + + /** + * @return string name of the record property that the relationship results will be assigned to. + */ + public function getProperty() + { + return $this->_property; + } + + /** + * @return TActiveRecord the active record instance that queried for its related records. + */ + public function getSourceRecord() + { + return $this->_record; + } + + /** + * @return array foreign key of this relations, the keys is dependent on the + * relationship type. + * @since 3.1.2 + */ + public function getRelationForeignKeys() + { + if($this->_fkeys===null) + $this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys(); + return $this->_fkeys; + } + + /** + * @return string HAS_MANY, HAS_ONE, or BELONGS_TO + */ + public function getRelationType() + { + return $this->_relation[0]; + } + + /** + * @return string foreign record class name. + */ + public function getForeignRecordClass() + { + return $this->_relation[1]; + } + + /** + * @return string foreign key field names, comma delimited. + * @since 3.1.2 + */ + public function getFkField() + { + return $this->_relation[2]; + } + + /** + * @return string the query condition for the relation as specified in RELATIONS + * @since 3.1.2 + */ + public function getCondition() + { + return isset($this->_relation[3])?$this->_relation[3]:null; + } + + /** + * @return array the query parameters for the relation as specified in RELATIONS + * @since 3.1.2 + */ + public function getParameters() + { + return isset($this->_relation[4])?$this->_relation[4]:array(); + } + + /** + * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set. + * @since 3.1.2 + */ + public function hasFkField() + { + $notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY; + return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); + } + + /** + * @return string the M-N relationship association table name. + */ + public function getAssociationTable() + { + return $this->_relation[2]; + } + + /** + * @return boolean true if the relationship is HAS_MANY and requires an association table. + */ + public function hasAssociationTable() + { + $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY; + return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); + } + + /** + * @return TActiveRecord corresponding relationship foreign object finder instance. + */ + public function getForeignRecordFinder() + { + return TActiveRecord::finder($this->getForeignRecordClass()); + } + + /** + * Creates and return the TActiveRecordRelation handler for specific relationships. + * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany, + * or TActiveRecordHasManyAssocation will be returned. + * @param TActiveRecordCriteria search criteria + * @return TActiveRecordRelation record relationship handler instnace. + * @throws TActiveRecordException if property is not defined or missing. + */ + public function getRelationHandler($criteria=null) + { + if(!$this->hasRecordRelation()) + { + throw new TActiveRecordException('ar_undefined_relation_prop', + $this->_property, get_class($this->_record), 'RELATIONS'); + } + if($criteria===null) + $criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters()); + switch($this->getRelationType()) + { + case TActiveRecord::HAS_MANY: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany'); + return new TActiveRecordHasMany($this, $criteria); + case TActiveRecord::MANY_TO_MANY: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation'); + return new TActiveRecordHasManyAssociation($this, $criteria); + case TActiveRecord::HAS_ONE: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne'); + return new TActiveRecordHasOne($this, $criteria); + case TActiveRecord::BELONGS_TO: + Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo'); + return new TActiveRecordBelongsTo($this, $criteria); + default: + throw new TActiveRecordException('ar_invalid_relationship'); + } + } + + /** + * @return TActiveRecordRelationCommand + */ + public function updateAssociatedRecords($updateBelongsTo=false) + { + $success=true; + foreach($this->_record->getRecordRelations() as $data) + { + list($property, $relation) = $data; + $belongsTo = $relation[0]==TActiveRecord::BELONGS_TO; + if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo)) + { + $obj = $this->getSourceRecord(); + if(!$this->isEmptyFkObject($obj->getColumnValue($property))) + { + $context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation); + $success = $context->getRelationHandler()->updateAssociatedRecords() && $success; + } + } + } + return $success; + } + + protected function isEmptyFkObject($obj) + { + if(is_object($obj)) + return $obj instanceof TList ? $obj->count() === 0 : false; + else if(is_array($obj)) + return count($obj)===0; + else + return empty($obj); + } +} + -- cgit v1.2.3