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 --- .../Exceptions/TActiveRecordException.php | 98 +-- .../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 ++++++------- .../Data/ActiveRecord/Scaffold/TScaffoldBase.php | 414 ++++++------ .../ActiveRecord/Scaffold/TScaffoldEditView.php | 616 ++++++++--------- .../ActiveRecord/Scaffold/TScaffoldListView.php | 608 ++++++++--------- .../Data/ActiveRecord/Scaffold/TScaffoldSearch.php | 296 ++++---- .../Data/ActiveRecord/Scaffold/TScaffoldView.php | 286 ++++---- .../Data/ActiveRecord/TActiveRecordConfig.php | 400 +++++------ .../Data/ActiveRecord/TActiveRecordCriteria.php | 72 +- .../Data/ActiveRecord/TActiveRecordManager.php | 324 ++++----- 15 files changed, 2816 insertions(+), 2816 deletions(-) (limited to 'framework/Data/ActiveRecord') diff --git a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php index a5d6405e..bcfbfbcb 100644 --- a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php +++ b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php @@ -1,49 +1,49 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord - */ - -/** - * Base exception class for Active Records. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord - * @since 3.1 - */ -class TActiveRecordException extends TDbException -{ - /** - * @return string path to the error message file - */ - protected function getErrorMessageFile() - { - $lang=Prado::getPreferredLanguage(); - $path = dirname(__FILE__); - $msgFile=$path.'/messages-'.$lang.'.txt'; - if(!is_file($msgFile)) - $msgFile=$path.'/messages.txt'; - return $msgFile; - } -} - -/** - * TActiveRecordConfigurationException class. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord - * @since 3.1 - */ -class TActiveRecordConfigurationException extends TActiveRecordException -{ - -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +/** + * Base exception class for Active Records. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordException extends TDbException +{ + /** + * @return string path to the error message file + */ + protected function getErrorMessageFile() + { + $lang=Prado::getPreferredLanguage(); + $path = dirname(__FILE__); + $msgFile=$path.'/messages-'.$lang.'.txt'; + if(!is_file($msgFile)) + $msgFile=$path.'/messages.txt'; + return $msgFile; + } +} + +/** + * TActiveRecordConfigurationException class. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordConfigurationException extends TActiveRecordException +{ + +} + 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); + } +} + diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php index d2e58bc3..68f5806a 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php @@ -1,207 +1,207 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - */ - -/** - * Include the base Active Record class. - */ -Prado::using('System.Data.ActiveRecord.TActiveRecord'); - -/** - * Base class for Active Record scaffold views. - * - * Provides common properties for all scaffold views (such as, TScaffoldListView, - * TScaffoldEditView, TScaffoldListView and TScaffoldView). - * - * During the OnPrRender stage the default css style file (filename style.css) - * is published and registered. To override the default style, provide your own stylesheet - * file explicitly. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - * @since 3.1 - */ -abstract class TScaffoldBase extends TTemplateControl -{ - /** - * @var TActiveRecord record instance (may be new or retrieved from db) - */ - private $_record; - - /** - * @return TDbMetaData table/view information - */ - protected function getTableInfo() - { - $finder = $this->getRecordFinder(); - $gateway = $finder->getRecordManager()->getRecordGateWay(); - return $gateway->getRecordTableInfo($finder); - } - - /** - * @param TActiveRecord record instance - * @return array record property values - */ - protected function getRecordPropertyValues($record) - { - $data = array(); - foreach($this->getTableInfo()->getColumns() as $name=>$column) - $data[] = $record->getColumnValue($name); - return $data; - } - - /** - * @param TActiveRecord record instance - * @return array record primary key values. - */ - protected function getRecordPkValues($record) - { - $data=array(); - foreach($this->getTableInfo()->getColumns() as $name=>$column) - { - if($column->getIsPrimaryKey()) - $data[] = $record->getColumnValue($name); - } - return $data; - } - - /** - * Name of the Active Record class to be viewed or scaffolded. - * @return string Active Record class name. - */ - public function getRecordClass() - { - return $this->getViewState('RecordClass'); - } - - /** - * Name of the Active Record class to be viewed or scaffolded. - * @param string Active Record class name. - */ - public function setRecordClass($value) - { - $this->setViewState('RecordClass', $value); - } - - /** - * Copy the view details from another scaffold view instance. - * @param TScaffoldBase scaffold view. - */ - protected function copyFrom(TScaffoldBase $obj) - { - $this->_record = $obj->_record; - $this->setRecordClass($obj->getRecordClass()); - $this->setEnableDefaultStyle($obj->getEnableDefaultStyle()); - } - - /** - * Unset the current record instance and table information. - */ - protected function clearRecordObject() - { - $this->_record=null; - } - - /** - * Gets the current Active Record instance. Creates new instance if the - * primary key value is null otherwise the record is fetched from the db. - * @param array primary key value - * @return TActiveRecord record instance - */ - protected function getRecordObject($pk=null) - { - if($this->_record===null) - { - if($pk!==null) - { - $this->_record=$this->getRecordFinder()->findByPk($pk); - if($this->_record===null) - throw new TConfigurationException('scaffold_invalid_record_pk', - $this->getRecordClass(), $pk); - } - else - { - $class = $this->getRecordClass(); - if($class!==null) - $this->_record=Prado::createComponent($class); - else - { - throw new TConfigurationException('scaffold_invalid_record_class', - $this->getRecordClass(),$this->getID()); - } - } - } - return $this->_record; - } - - /** - * @param TActiveRecord Active Record instance. - */ - protected function setRecordObject(TActiveRecord $value) - { - $this->_record=$value; - } - - /** - * @return TActiveRecord Active Record finder instance - */ - protected function getRecordFinder() - { - return TActiveRecord::finder($this->getRecordClass()); - } - - /** - * @return string default scaffold stylesheet name - */ - public function getDefaultStyle() - { - return $this->getViewState('DefaultStyle', 'style'); - } - - /** - * @param string default scaffold stylesheet name - */ - public function setDefaultStyle($value) - { - $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style'); - } - - /** - * @return boolean enable default stylesheet, default is true. - */ - public function getEnableDefaultStyle() - { - return $this->getViewState('EnableDefaultStyle', true); - } - - /** - * @param boolean enable default stylesheet, default is true. - */ - public function setEnableDefaultStyle($value) - { - return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true); - } - - /** - * Publish the default stylesheet file. - */ - public function onPreRender($param) - { - parent::onPreRender($param); - if($this->getEnableDefaultStyle()) - { - $url = $this->publishAsset($this->getDefaultStyle().'.css'); - $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); - } - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Include the base Active Record class. + */ +Prado::using('System.Data.ActiveRecord.TActiveRecord'); + +/** + * Base class for Active Record scaffold views. + * + * Provides common properties for all scaffold views (such as, TScaffoldListView, + * TScaffoldEditView, TScaffoldListView and TScaffoldView). + * + * During the OnPrRender stage the default css style file (filename style.css) + * is published and registered. To override the default style, provide your own stylesheet + * file explicitly. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +abstract class TScaffoldBase extends TTemplateControl +{ + /** + * @var TActiveRecord record instance (may be new or retrieved from db) + */ + private $_record; + + /** + * @return TDbMetaData table/view information + */ + protected function getTableInfo() + { + $finder = $this->getRecordFinder(); + $gateway = $finder->getRecordManager()->getRecordGateWay(); + return $gateway->getRecordTableInfo($finder); + } + + /** + * @param TActiveRecord record instance + * @return array record property values + */ + protected function getRecordPropertyValues($record) + { + $data = array(); + foreach($this->getTableInfo()->getColumns() as $name=>$column) + $data[] = $record->getColumnValue($name); + return $data; + } + + /** + * @param TActiveRecord record instance + * @return array record primary key values. + */ + protected function getRecordPkValues($record) + { + $data=array(); + foreach($this->getTableInfo()->getColumns() as $name=>$column) + { + if($column->getIsPrimaryKey()) + $data[] = $record->getColumnValue($name); + } + return $data; + } + + /** + * Name of the Active Record class to be viewed or scaffolded. + * @return string Active Record class name. + */ + public function getRecordClass() + { + return $this->getViewState('RecordClass'); + } + + /** + * Name of the Active Record class to be viewed or scaffolded. + * @param string Active Record class name. + */ + public function setRecordClass($value) + { + $this->setViewState('RecordClass', $value); + } + + /** + * Copy the view details from another scaffold view instance. + * @param TScaffoldBase scaffold view. + */ + protected function copyFrom(TScaffoldBase $obj) + { + $this->_record = $obj->_record; + $this->setRecordClass($obj->getRecordClass()); + $this->setEnableDefaultStyle($obj->getEnableDefaultStyle()); + } + + /** + * Unset the current record instance and table information. + */ + protected function clearRecordObject() + { + $this->_record=null; + } + + /** + * Gets the current Active Record instance. Creates new instance if the + * primary key value is null otherwise the record is fetched from the db. + * @param array primary key value + * @return TActiveRecord record instance + */ + protected function getRecordObject($pk=null) + { + if($this->_record===null) + { + if($pk!==null) + { + $this->_record=$this->getRecordFinder()->findByPk($pk); + if($this->_record===null) + throw new TConfigurationException('scaffold_invalid_record_pk', + $this->getRecordClass(), $pk); + } + else + { + $class = $this->getRecordClass(); + if($class!==null) + $this->_record=Prado::createComponent($class); + else + { + throw new TConfigurationException('scaffold_invalid_record_class', + $this->getRecordClass(),$this->getID()); + } + } + } + return $this->_record; + } + + /** + * @param TActiveRecord Active Record instance. + */ + protected function setRecordObject(TActiveRecord $value) + { + $this->_record=$value; + } + + /** + * @return TActiveRecord Active Record finder instance + */ + protected function getRecordFinder() + { + return TActiveRecord::finder($this->getRecordClass()); + } + + /** + * @return string default scaffold stylesheet name + */ + public function getDefaultStyle() + { + return $this->getViewState('DefaultStyle', 'style'); + } + + /** + * @param string default scaffold stylesheet name + */ + public function setDefaultStyle($value) + { + $this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style'); + } + + /** + * @return boolean enable default stylesheet, default is true. + */ + public function getEnableDefaultStyle() + { + return $this->getViewState('EnableDefaultStyle', true); + } + + /** + * @param boolean enable default stylesheet, default is true. + */ + public function setEnableDefaultStyle($value) + { + return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true); + } + + /** + * Publish the default stylesheet file. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if($this->getEnableDefaultStyle()) + { + $url = $this->publishAsset($this->getDefaultStyle().'.css'); + $this->getPage()->getClientScript()->registerStyleSheetFile($url,$url); + } + } +} + diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php index c0547627..166e3360 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php @@ -1,309 +1,309 @@ - - * @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.Scaffold - */ - -/** - * Load scaffold base. - */ -Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); - -/** - * Template control for editing an Active Record instance. - * The RecordClass determines the Active Record class to be edited. - * A particular record can be edited by specifying the {@link setRecordPk RecordPk} - * value (may be an array for composite keys). - * - * The default editor input controls are created based on the column types. - * The editor layout can be specified by a renderer by set the value - * of the {@link setEditRenderer EditRenderer} property to the class name of a - * class that implements TScaffoldEditRenderer. A renderer is an external - * template control that implements IScaffoldEditRenderer. - * - * The Data of the IScaffoldEditRenderer will be set as the current Active - * Record to be edited. The UpdateRecord() method of IScaffoldEditRenderer - * is called when request to save the record is requested. - * - * Validators in the custom external editor template should have the - * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the - * value of the {@link getValidationGroup} of the TScaffoldEditView instance - * (the edit view instance is the Parent of the IScaffoldEditRenderer in most - * cases. - * - * Cosmetic changes to the default editor should be done using Cascading Stylesheets. - * For example, a particular field/property can be hidden by specifying "display:none" for - * the corresponding style (each field/property has unique Css class name as "property_xxx", where - * xxx is the property name). - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - * @since 3.1 - */ -class TScaffoldEditView extends TScaffoldBase -{ - /** - * @var IScaffoldEditRenderer custom scaffold edit renderer - */ - private $_editRenderer; - - /** - * Initialize the editor form if it is Visible. - */ - public function onLoad($param) - { - if($this->getVisible()) - $this->initializeEditForm(); - } - - /** - * @return string the class name for scaffold editor. Defaults to empty, meaning not set. - */ - public function getEditRenderer() - { - return $this->getViewState('EditRenderer', ''); - } - - /** - * @param string the class name for scaffold editor. Defaults to empty, meaning not set. - */ - public function setEditRenderer($value) - { - $this->setViewState('EditRenderer', $value, ''); - } - - /** - * @param array Active Record primary key value to be edited. - */ - public function setRecordPk($value) - { - $this->clearRecordObject(); - $val = TPropertyValue::ensureArray($value); - $this->setViewState('PK', count($val) > 0 ? $val : null); - } - - /** - * @return array Active Record primary key value. - */ - public function getRecordPk() - { - return $this->getViewState('PK'); - } - - /** - * @return TActiveRecord current Active Record instance - */ - protected function getCurrentRecord() - { - return $this->getRecordObject($this->getRecordPk()); - } - - /** - * Initialize the editor form - */ - public function initializeEditForm() - { - $record = $this->getCurrentRecord(); - $classPath = $this->getEditRenderer(); - if($classPath === '') - { - $columns = $this->getTableInfo()->getColumns(); - $this->getInputRepeater()->setDataSource($columns); - $this->getInputRepeater()->dataBind(); - } - else - { - if($this->_editRenderer===null) - $this->createEditRenderer($record, $classPath); - else - $this->_editRenderer->setData($record); - } - } - - /** - * Instantiate the external edit renderer. - * @param TActiveRecord record to be edited - * @param string external edit renderer class name. - * @throws TConfigurationException raised when renderer is not an - * instance of IScaffoldEditRenderer. - */ - protected function createEditRenderer($record, $classPath) - { - $this->_editRenderer = Prado::createComponent($classPath); - if($this->_editRenderer instanceof IScaffoldEditRenderer) - { - $index = $this->getControls()->remove($this->getInputRepeater()); - $this->getControls()->insertAt($index,$this->_editRenderer); - $this->_editRenderer->setData($record); - } - else - { - throw new TConfigurationException( - 'scaffold_invalid_edit_renderer', $this->getID(), get_class($record)); - } - } - - /** - * Initialize the default editor using the scaffold input builder. - */ - protected function createRepeaterEditItem($sender, $param) - { - $type = $param->getItem()->getItemType(); - if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) - { - $item = $param->getItem(); - $column = $item->getDataItem(); - if($column===null) - return; - - $record = $this->getCurrentRecord(); - $builder = $this->getScaffoldInputBuilder($record); - $builder->createScaffoldInput($this, $item, $column, $record); - } - } - - /** - * Bubble the command name event. Stops bubbling when the page validator false. - * Otherwise, the bubble event is continued. - */ - public function bubbleEvent($sender, $param) - { - switch(strtolower($param->getCommandName())) - { - case 'save': - return $this->doSave() ? false : true; - case 'clear': - $this->setRecordPk(null); - $this->initializeEditForm(); - return false; - default: - return false; - } - } - - /** - * Check the validators, then tries to save the record. - * @return boolean true if the validators are true, false otherwise. - */ - protected function doSave() - { - if($this->getPage()->getIsValid()) - { - $record = $this->getCurrentRecord(); - if($this->_editRenderer===null) - { - $table = $this->getTableInfo(); - $builder = $this->getScaffoldInputBuilder($record); - foreach($this->getInputRepeater()->getItems() as $item) - { - $column = $table->getColumn($item->getCustomData()); - $builder->loadScaffoldInput($this, $item, $column, $record); - } - } - else - { - $this->_editRenderer->updateRecord($record); - } - $record->save(); - return true; - } - else if($this->_editRenderer!==null) - { - //preserve the form data. - $this->_editRenderer->updateRecord($this->getCurrentRecord()); - } - - return false; - } - - /** - * @return TRepeater default editor input controls repeater - */ - protected function getInputRepeater() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_repeater'); - } - - /** - * @return TButton Button triggered to save the Active Record. - */ - public function getSaveButton() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_save'); - } - - /** - * @return TButton Button to clear the editor inputs. - */ - public function getClearButton() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_clear'); - } - - /** - * @return TButton Button to cancel the edit action (e.g. hide the edit view). - */ - public function getCancelButton() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_cancel'); - } - - /** - * Create the default scaffold editor control factory. - * @param TActiveRecord record instance. - * @return TScaffoldInputBase scaffold editor control factory. - */ - protected function getScaffoldInputBuilder($record) - { - static $_builders=array(); - $class = get_class($record); - if(!isset($_builders[$class])) - { - Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase'); - $_builders[$class] = TScaffoldInputBase::createInputBuilder($record); - } - return $_builders[$class]; - } - - /** - * @return string editor validation group name. - */ - public function getValidationGroup() - { - return 'group_'.$this->getUniqueID(); - } -} - -/** - * IScaffoldEditRenderer interface. - * - * IScaffoldEditRenderer defines the interface that an edit renderer - * needs to implement. Besides the {@link getData Data} property, an edit - * renderer also needs to provide {@link updateRecord updateRecord} method - * that is called before the save() method is called on the TActiveRecord. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - * @since 3.1 - */ -interface IScaffoldEditRenderer extends IDataRenderer -{ - /** - * This method should update the record with the user input data. - * @param TActiveRecord record to be saved. - */ - public function updateRecord($record); -} - + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Load scaffold base. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * Template control for editing an Active Record instance. + * The RecordClass determines the Active Record class to be edited. + * A particular record can be edited by specifying the {@link setRecordPk RecordPk} + * value (may be an array for composite keys). + * + * The default editor input controls are created based on the column types. + * The editor layout can be specified by a renderer by set the value + * of the {@link setEditRenderer EditRenderer} property to the class name of a + * class that implements TScaffoldEditRenderer. A renderer is an external + * template control that implements IScaffoldEditRenderer. + * + * The Data of the IScaffoldEditRenderer will be set as the current Active + * Record to be edited. The UpdateRecord() method of IScaffoldEditRenderer + * is called when request to save the record is requested. + * + * Validators in the custom external editor template should have the + * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the + * value of the {@link getValidationGroup} of the TScaffoldEditView instance + * (the edit view instance is the Parent of the IScaffoldEditRenderer in most + * cases. + * + * Cosmetic changes to the default editor should be done using Cascading Stylesheets. + * For example, a particular field/property can be hidden by specifying "display:none" for + * the corresponding style (each field/property has unique Css class name as "property_xxx", where + * xxx is the property name). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldEditView extends TScaffoldBase +{ + /** + * @var IScaffoldEditRenderer custom scaffold edit renderer + */ + private $_editRenderer; + + /** + * Initialize the editor form if it is Visible. + */ + public function onLoad($param) + { + if($this->getVisible()) + $this->initializeEditForm(); + } + + /** + * @return string the class name for scaffold editor. Defaults to empty, meaning not set. + */ + public function getEditRenderer() + { + return $this->getViewState('EditRenderer', ''); + } + + /** + * @param string the class name for scaffold editor. Defaults to empty, meaning not set. + */ + public function setEditRenderer($value) + { + $this->setViewState('EditRenderer', $value, ''); + } + + /** + * @param array Active Record primary key value to be edited. + */ + public function setRecordPk($value) + { + $this->clearRecordObject(); + $val = TPropertyValue::ensureArray($value); + $this->setViewState('PK', count($val) > 0 ? $val : null); + } + + /** + * @return array Active Record primary key value. + */ + public function getRecordPk() + { + return $this->getViewState('PK'); + } + + /** + * @return TActiveRecord current Active Record instance + */ + protected function getCurrentRecord() + { + return $this->getRecordObject($this->getRecordPk()); + } + + /** + * Initialize the editor form + */ + public function initializeEditForm() + { + $record = $this->getCurrentRecord(); + $classPath = $this->getEditRenderer(); + if($classPath === '') + { + $columns = $this->getTableInfo()->getColumns(); + $this->getInputRepeater()->setDataSource($columns); + $this->getInputRepeater()->dataBind(); + } + else + { + if($this->_editRenderer===null) + $this->createEditRenderer($record, $classPath); + else + $this->_editRenderer->setData($record); + } + } + + /** + * Instantiate the external edit renderer. + * @param TActiveRecord record to be edited + * @param string external edit renderer class name. + * @throws TConfigurationException raised when renderer is not an + * instance of IScaffoldEditRenderer. + */ + protected function createEditRenderer($record, $classPath) + { + $this->_editRenderer = Prado::createComponent($classPath); + if($this->_editRenderer instanceof IScaffoldEditRenderer) + { + $index = $this->getControls()->remove($this->getInputRepeater()); + $this->getControls()->insertAt($index,$this->_editRenderer); + $this->_editRenderer->setData($record); + } + else + { + throw new TConfigurationException( + 'scaffold_invalid_edit_renderer', $this->getID(), get_class($record)); + } + } + + /** + * Initialize the default editor using the scaffold input builder. + */ + protected function createRepeaterEditItem($sender, $param) + { + $type = $param->getItem()->getItemType(); + if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) + { + $item = $param->getItem(); + $column = $item->getDataItem(); + if($column===null) + return; + + $record = $this->getCurrentRecord(); + $builder = $this->getScaffoldInputBuilder($record); + $builder->createScaffoldInput($this, $item, $column, $record); + } + } + + /** + * Bubble the command name event. Stops bubbling when the page validator false. + * Otherwise, the bubble event is continued. + */ + public function bubbleEvent($sender, $param) + { + switch(strtolower($param->getCommandName())) + { + case 'save': + return $this->doSave() ? false : true; + case 'clear': + $this->setRecordPk(null); + $this->initializeEditForm(); + return false; + default: + return false; + } + } + + /** + * Check the validators, then tries to save the record. + * @return boolean true if the validators are true, false otherwise. + */ + protected function doSave() + { + if($this->getPage()->getIsValid()) + { + $record = $this->getCurrentRecord(); + if($this->_editRenderer===null) + { + $table = $this->getTableInfo(); + $builder = $this->getScaffoldInputBuilder($record); + foreach($this->getInputRepeater()->getItems() as $item) + { + $column = $table->getColumn($item->getCustomData()); + $builder->loadScaffoldInput($this, $item, $column, $record); + } + } + else + { + $this->_editRenderer->updateRecord($record); + } + $record->save(); + return true; + } + else if($this->_editRenderer!==null) + { + //preserve the form data. + $this->_editRenderer->updateRecord($this->getCurrentRecord()); + } + + return false; + } + + /** + * @return TRepeater default editor input controls repeater + */ + protected function getInputRepeater() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_repeater'); + } + + /** + * @return TButton Button triggered to save the Active Record. + */ + public function getSaveButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_save'); + } + + /** + * @return TButton Button to clear the editor inputs. + */ + public function getClearButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_clear'); + } + + /** + * @return TButton Button to cancel the edit action (e.g. hide the edit view). + */ + public function getCancelButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_cancel'); + } + + /** + * Create the default scaffold editor control factory. + * @param TActiveRecord record instance. + * @return TScaffoldInputBase scaffold editor control factory. + */ + protected function getScaffoldInputBuilder($record) + { + static $_builders=array(); + $class = get_class($record); + if(!isset($_builders[$class])) + { + Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase'); + $_builders[$class] = TScaffoldInputBase::createInputBuilder($record); + } + return $_builders[$class]; + } + + /** + * @return string editor validation group name. + */ + public function getValidationGroup() + { + return 'group_'.$this->getUniqueID(); + } +} + +/** + * IScaffoldEditRenderer interface. + * + * IScaffoldEditRenderer defines the interface that an edit renderer + * needs to implement. Besides the {@link getData Data} property, an edit + * renderer also needs to provide {@link updateRecord updateRecord} method + * that is called before the save() method is called on the TActiveRecord. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +interface IScaffoldEditRenderer extends IDataRenderer +{ + /** + * This method should update the record with the user input data. + * @param TActiveRecord record to be saved. + */ + public function updateRecord($record); +} + diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php index 953420e3..bed5bf88 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php @@ -1,306 +1,306 @@ - - * @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.Scaffold - */ - -/** - * Load the scaffold base class. - */ -Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); - -/** - * TScaffoldListView displays a list of Active Records. - * - * The {@link getHeader Header} property is a TRepeater displaying the - * Active Record property/field names. The {@link getSort Sort} property - * is a drop down list displaying the combination of properties and its possible - * ordering. The {@link getPager Pager} property is a TPager control displaying - * the links and/or buttons that navigate to different pages in the Active Record data. - * The {@link getList List} property is a TRepeater that renders a row of - * Active Record data. - * - * Custom rendering of the each Active Record can be achieved by specifying - * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List} - * repeater. - * - * The TScaffoldListView will listen for two command events named "delete" and - * "edit". A "delete" command will delete a the record for the row where the - * "delete" command is originates. An "edit" command will push - * the record data to be edited by a TScaffoldEditView with ID specified by the - * {@link setEditViewID EditViewID}. - * - * Additional {@link setSearchCondition SearchCondition} and - * {@link setSearchParameters SearchParameters} (takes array values) can be - * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition} - * will be used as the Condition property of TActiveRecordCriteria, and similarly - * the {@link setSearchParameters SearchParameters} will be the corresponding - * Parameters property of TActiveRecordCriteria. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - * @since 3.1 - */ -class TScaffoldListView extends TScaffoldBase -{ - /** - * Initialize the sort drop down list and the column names repeater. - */ - protected function initializeSort() - { - $table = $this->getTableInfo(); - $sorts = array('Sort By', str_repeat('-',15)); - $headers = array(); - foreach($table->getColumns() as $name=>$colum) - { - $fname = ucwords(str_replace('_', ' ', $name)); - $sorts[$name.' ASC'] = $fname .' Ascending'; - $sorts[$name.' DESC'] = $fname .' Descending'; - $headers[] = $fname ; - } - $this->_sort->setDataSource($sorts); - $this->_sort->dataBind(); - $this->_header->setDataSource($headers); - $this->_header->dataBind(); - } - - /** - * Loads and display the data. - */ - public function onPreRender($param) - { - parent::onPreRender($param); - if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass()) - { - $this->initializeSort(); - $this->setViewState('CurrentClass', $this->getRecordClass()); - } - $this->loadRecordData(); - } - - /** - * Fetch the records and data bind it to the list. - */ - protected function loadRecordData() - { - $search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); - $this->_list->setVirtualItemCount($this->getRecordFinder()->count($search)); - $finder = $this->getRecordFinder(); - $criteria = $this->getRecordCriteria(); - $this->_list->setDataSource($finder->findAll($criteria)); - $this->_list->dataBind(); - } - - /** - * @return TActiveRecordCriteria sort/search/paging criteria - */ - protected function getRecordCriteria() - { - $total = $this->_list->getVirtualItemCount(); - $limit = $this->_list->getPageSize(); - $offset = $this->_list->getCurrentPageIndex()*$limit; - if($offset + $limit > $total) - $limit = $total - $offset; - $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); - if($limit > 0) - { - $criteria->setLimit($limit); - if($offset <= $total) - $criteria->setOffset($offset); - } - $order = explode(' ',$this->_sort->getSelectedValue(), 2); - if(is_array($order) && count($order) === 2) - $criteria->OrdersBy[$order[0]] = $order[1]; - return $criteria; - } - - /** - * @param string search condition, the SQL string after the WHERE clause. - */ - public function setSearchCondition($value) - { - $this->setViewState('SearchCondition', $value); - } - - /** - * @param string SQL search condition for list display. - */ - public function getSearchCondition() - { - return $this->getViewState('SearchCondition'); - } - - /** - * @param array search parameters - */ - public function setSearchParameters($value) - { - $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array()); - } - - /** - * @return array search parameters - */ - public function getSearchParameters() - { - return $this->getViewState('SearchParameters', array()); - } - - /** - * Continue bubbling the "edit" command, "delete" command is handled in this class. - */ - public function bubbleEvent($sender, $param) - { - switch(strtolower($param->getCommandName())) - { - case 'delete': - return $this->deleteRecord($sender, $param); - case 'edit': - $this->initializeEdit($sender, $param); - } - $this->raiseBubbleEvent($this, $param); - return true; - } - - /** - * Initialize the edit view control form when EditViewID is set. - */ - protected function initializeEdit($sender, $param) - { - if(($ctrl=$this->getEditViewControl())!==null) - { - if($param instanceof TRepeaterCommandEventParameter) - { - $pk = $param->getItem()->getCustomData(); - $ctrl->setRecordPk($pk); - $ctrl->initializeEditForm(); - } - } - } - - /** - * Deletes an Active Record. - */ - protected function deleteRecord($sender, $param) - { - if($param instanceof TRepeaterCommandEventParameter) - { - $pk = $param->getItem()->getCustomData(); - $this->getRecordFinder()->deleteByPk($pk); - } - } - - /** - * Initialize the default display for each Active Record item. - */ - protected function listItemCreated($sender, $param) - { - $item = $param->getItem(); - if($item instanceof IItemDataRenderer) - { - $type = $item->getItemType(); - if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) - $this->populateField($sender, $param); - } - } - - /** - * Sets the Record primary key to the current repeater item's CustomData. - * Binds the inner repeater with properties of the current Active Record. - */ - protected function populateField($sender, $param) - { - $item = $param->getItem(); - if(($data = $item->getData()) !== null) - { - $item->setCustomData($this->getRecordPkValues($data)); - if(($prop = $item->findControl('_properties'))!==null) - { - $item->_properties->setDataSource($this->getRecordPropertyValues($data)); - $item->_properties->dataBind(); - } - } - } - - /** - * Updates repeater page index with the pager new index value. - */ - protected function pageChanged($sender, $param) - { - $this->_list->setCurrentPageIndex($param->getNewPageIndex()); - } - - /** - * @return TRepeater Repeater control for Active Record instances. - */ - public function getList() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_list'); - } - - /** - * @return TPager List pager control. - */ - public function getPager() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_pager'); - } - - /** - * @return TDropDownList Control that displays and controls the record ordering. - */ - public function getSort() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_sort'); - } - - /** - * @return TRepeater Repeater control for record property names. - */ - public function getHeader() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_header'); - } - - /** - * @return string TScaffoldEditView control ID for editing selected Active Record. - */ - public function getEditViewID() - { - return $this->getViewState('EditViewID'); - } - - /** - * @param string TScaffoldEditView control ID for editing selected Active Record. - */ - public function setEditViewID($value) - { - $this->setViewState('EditViewID', $value); - } - - /** - * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set. - */ - protected function getEditViewControl() - { - if(($id=$this->getEditViewID())!==null) - { - $ctrl = $this->getParent()->findControl($id); - if($ctrl===null) - throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id); - return $ctrl; - } - } -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Load the scaffold base class. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * TScaffoldListView displays a list of Active Records. + * + * The {@link getHeader Header} property is a TRepeater displaying the + * Active Record property/field names. The {@link getSort Sort} property + * is a drop down list displaying the combination of properties and its possible + * ordering. The {@link getPager Pager} property is a TPager control displaying + * the links and/or buttons that navigate to different pages in the Active Record data. + * The {@link getList List} property is a TRepeater that renders a row of + * Active Record data. + * + * Custom rendering of the each Active Record can be achieved by specifying + * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List} + * repeater. + * + * The TScaffoldListView will listen for two command events named "delete" and + * "edit". A "delete" command will delete a the record for the row where the + * "delete" command is originates. An "edit" command will push + * the record data to be edited by a TScaffoldEditView with ID specified by the + * {@link setEditViewID EditViewID}. + * + * Additional {@link setSearchCondition SearchCondition} and + * {@link setSearchParameters SearchParameters} (takes array values) can be + * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition} + * will be used as the Condition property of TActiveRecordCriteria, and similarly + * the {@link setSearchParameters SearchParameters} will be the corresponding + * Parameters property of TActiveRecordCriteria. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldListView extends TScaffoldBase +{ + /** + * Initialize the sort drop down list and the column names repeater. + */ + protected function initializeSort() + { + $table = $this->getTableInfo(); + $sorts = array('Sort By', str_repeat('-',15)); + $headers = array(); + foreach($table->getColumns() as $name=>$colum) + { + $fname = ucwords(str_replace('_', ' ', $name)); + $sorts[$name.' ASC'] = $fname .' Ascending'; + $sorts[$name.' DESC'] = $fname .' Descending'; + $headers[] = $fname ; + } + $this->_sort->setDataSource($sorts); + $this->_sort->dataBind(); + $this->_header->setDataSource($headers); + $this->_header->dataBind(); + } + + /** + * Loads and display the data. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass()) + { + $this->initializeSort(); + $this->setViewState('CurrentClass', $this->getRecordClass()); + } + $this->loadRecordData(); + } + + /** + * Fetch the records and data bind it to the list. + */ + protected function loadRecordData() + { + $search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); + $this->_list->setVirtualItemCount($this->getRecordFinder()->count($search)); + $finder = $this->getRecordFinder(); + $criteria = $this->getRecordCriteria(); + $this->_list->setDataSource($finder->findAll($criteria)); + $this->_list->dataBind(); + } + + /** + * @return TActiveRecordCriteria sort/search/paging criteria + */ + protected function getRecordCriteria() + { + $total = $this->_list->getVirtualItemCount(); + $limit = $this->_list->getPageSize(); + $offset = $this->_list->getCurrentPageIndex()*$limit; + if($offset + $limit > $total) + $limit = $total - $offset; + $criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters()); + if($limit > 0) + { + $criteria->setLimit($limit); + if($offset <= $total) + $criteria->setOffset($offset); + } + $order = explode(' ',$this->_sort->getSelectedValue(), 2); + if(is_array($order) && count($order) === 2) + $criteria->OrdersBy[$order[0]] = $order[1]; + return $criteria; + } + + /** + * @param string search condition, the SQL string after the WHERE clause. + */ + public function setSearchCondition($value) + { + $this->setViewState('SearchCondition', $value); + } + + /** + * @param string SQL search condition for list display. + */ + public function getSearchCondition() + { + return $this->getViewState('SearchCondition'); + } + + /** + * @param array search parameters + */ + public function setSearchParameters($value) + { + $this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array()); + } + + /** + * @return array search parameters + */ + public function getSearchParameters() + { + return $this->getViewState('SearchParameters', array()); + } + + /** + * Continue bubbling the "edit" command, "delete" command is handled in this class. + */ + public function bubbleEvent($sender, $param) + { + switch(strtolower($param->getCommandName())) + { + case 'delete': + return $this->deleteRecord($sender, $param); + case 'edit': + $this->initializeEdit($sender, $param); + } + $this->raiseBubbleEvent($this, $param); + return true; + } + + /** + * Initialize the edit view control form when EditViewID is set. + */ + protected function initializeEdit($sender, $param) + { + if(($ctrl=$this->getEditViewControl())!==null) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $pk = $param->getItem()->getCustomData(); + $ctrl->setRecordPk($pk); + $ctrl->initializeEditForm(); + } + } + } + + /** + * Deletes an Active Record. + */ + protected function deleteRecord($sender, $param) + { + if($param instanceof TRepeaterCommandEventParameter) + { + $pk = $param->getItem()->getCustomData(); + $this->getRecordFinder()->deleteByPk($pk); + } + } + + /** + * Initialize the default display for each Active Record item. + */ + protected function listItemCreated($sender, $param) + { + $item = $param->getItem(); + if($item instanceof IItemDataRenderer) + { + $type = $item->getItemType(); + if($type==TListItemType::Item || $type==TListItemType::AlternatingItem) + $this->populateField($sender, $param); + } + } + + /** + * Sets the Record primary key to the current repeater item's CustomData. + * Binds the inner repeater with properties of the current Active Record. + */ + protected function populateField($sender, $param) + { + $item = $param->getItem(); + if(($data = $item->getData()) !== null) + { + $item->setCustomData($this->getRecordPkValues($data)); + if(($prop = $item->findControl('_properties'))!==null) + { + $item->_properties->setDataSource($this->getRecordPropertyValues($data)); + $item->_properties->dataBind(); + } + } + } + + /** + * Updates repeater page index with the pager new index value. + */ + protected function pageChanged($sender, $param) + { + $this->_list->setCurrentPageIndex($param->getNewPageIndex()); + } + + /** + * @return TRepeater Repeater control for Active Record instances. + */ + public function getList() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_list'); + } + + /** + * @return TPager List pager control. + */ + public function getPager() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_pager'); + } + + /** + * @return TDropDownList Control that displays and controls the record ordering. + */ + public function getSort() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_sort'); + } + + /** + * @return TRepeater Repeater control for record property names. + */ + public function getHeader() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_header'); + } + + /** + * @return string TScaffoldEditView control ID for editing selected Active Record. + */ + public function getEditViewID() + { + return $this->getViewState('EditViewID'); + } + + /** + * @param string TScaffoldEditView control ID for editing selected Active Record. + */ + public function setEditViewID($value) + { + $this->setViewState('EditViewID', $value); + } + + /** + * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set. + */ + protected function getEditViewControl() + { + if(($id=$this->getEditViewID())!==null) + { + $ctrl = $this->getParent()->findControl($id); + if($ctrl===null) + throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id); + return $ctrl; + } + } +} diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php index bd679c94..40335085 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php @@ -1,150 +1,150 @@ - - * @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.Scaffold - */ - -/** - * Import the scaffold base. - */ -Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); - -/** - * TScaffoldSearch provide a simple textbox and a button that is used - * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}. - * - * The {@link getSearchText SearchText} property is a TTextBox and the - * {@link getSearchButton SearchButton} property is a TButton with label value "Search". - * - * Searchable fields of the Active Record can be restricted by specifying - * a comma delimited string of allowable fields in the - * {@link setSearchableFields SearchableFields} property. The default is null, - * meaning that most text type fields are searched (the default searchable fields - * are database dependent). - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - * @since 3.1 - */ -class TScaffoldSearch extends TScaffoldBase -{ - /** - * @var TScaffoldListView the scaffold list view. - */ - private $_list; - - /** - * @return TScaffoldListView the scaffold list view this search box belongs to. - */ - protected function getListView() - { - if($this->_list===null && ($id = $this->getListViewID()) !== null) - { - $this->_list = $this->getParent()->findControl($id); - if($this->_list ===null) - throw new TConfigurationException('scaffold_unable_to_find_list_view', $id); - } - return $this->_list; - } - - /** - * @param string ID of the TScaffoldListView this search control belongs to. - */ - public function setListViewID($value) - { - $this->setViewState('ListViewID', $value); - } - - /** - * @return string ID of the TScaffoldListView this search control belongs to. - */ - public function getListViewID() - { - return $this->getViewState('ListViewID'); - } - - /** - * Sets the SearchCondition of the TScaffoldListView as the search terms - * given by the text of the search text box. - */ - public function bubbleEvent($sender, $param) - { - if(strtolower($param->getCommandName())==='search') - { - if(($list = $this->getListView()) !== null) - { - $list->setSearchCondition($this->createSearchCondition()); - return false; - } - } - $this->raiseBubbleEvent($this, $param); - return true; - } - - /** - * @return string the search criteria for the search terms in the search text box. - */ - protected function createSearchCondition() - { - $table = $this->getTableInfo(); - if(strlen($str=$this->getSearchText()->getText()) > 0) - { - $builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection()); - return $builder->getSearchExpression($this->getFields(), $str); - } - } - - /** - * @return array list of fields to be searched. - */ - protected function getFields() - { - if(strlen(trim($str=$this->getSearchableFields()))>0) - $fields = preg_split('/\s*,\s*/', $str); - else - $fields = $this->getTableInfo()->getColumns()->getKeys(); - return $fields; - } - - /** - * @return string comma delimited list of fields that may be searched. - */ - public function getSearchableFields() - { - return $this->getViewState('SearchableFields',''); - } - - /** - * @param string comma delimited list of fields that may be searched. - */ - public function setSearchableFields($value) - { - $this->setViewState('SearchableFields', $value, ''); - } - - /** - * @return TButton button with default label "Search". - */ - public function getSearchButton() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_search'); - } - - /** - * @return TTextBox search text box. - */ - public function getSearchText() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_textbox'); - } -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Import the scaffold base. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); + +/** + * TScaffoldSearch provide a simple textbox and a button that is used + * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}. + * + * The {@link getSearchText SearchText} property is a TTextBox and the + * {@link getSearchButton SearchButton} property is a TButton with label value "Search". + * + * Searchable fields of the Active Record can be restricted by specifying + * a comma delimited string of allowable fields in the + * {@link setSearchableFields SearchableFields} property. The default is null, + * meaning that most text type fields are searched (the default searchable fields + * are database dependent). + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.1 + */ +class TScaffoldSearch extends TScaffoldBase +{ + /** + * @var TScaffoldListView the scaffold list view. + */ + private $_list; + + /** + * @return TScaffoldListView the scaffold list view this search box belongs to. + */ + protected function getListView() + { + if($this->_list===null && ($id = $this->getListViewID()) !== null) + { + $this->_list = $this->getParent()->findControl($id); + if($this->_list ===null) + throw new TConfigurationException('scaffold_unable_to_find_list_view', $id); + } + return $this->_list; + } + + /** + * @param string ID of the TScaffoldListView this search control belongs to. + */ + public function setListViewID($value) + { + $this->setViewState('ListViewID', $value); + } + + /** + * @return string ID of the TScaffoldListView this search control belongs to. + */ + public function getListViewID() + { + return $this->getViewState('ListViewID'); + } + + /** + * Sets the SearchCondition of the TScaffoldListView as the search terms + * given by the text of the search text box. + */ + public function bubbleEvent($sender, $param) + { + if(strtolower($param->getCommandName())==='search') + { + if(($list = $this->getListView()) !== null) + { + $list->setSearchCondition($this->createSearchCondition()); + return false; + } + } + $this->raiseBubbleEvent($this, $param); + return true; + } + + /** + * @return string the search criteria for the search terms in the search text box. + */ + protected function createSearchCondition() + { + $table = $this->getTableInfo(); + if(strlen($str=$this->getSearchText()->getText()) > 0) + { + $builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection()); + return $builder->getSearchExpression($this->getFields(), $str); + } + } + + /** + * @return array list of fields to be searched. + */ + protected function getFields() + { + if(strlen(trim($str=$this->getSearchableFields()))>0) + $fields = preg_split('/\s*,\s*/', $str); + else + $fields = $this->getTableInfo()->getColumns()->getKeys(); + return $fields; + } + + /** + * @return string comma delimited list of fields that may be searched. + */ + public function getSearchableFields() + { + return $this->getViewState('SearchableFields',''); + } + + /** + * @param string comma delimited list of fields that may be searched. + */ + public function setSearchableFields($value) + { + $this->setViewState('SearchableFields', $value, ''); + } + + /** + * @return TButton button with default label "Search". + */ + public function getSearchButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + + /** + * @return TTextBox search text box. + */ + public function getSearchText() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_textbox'); + } +} diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php index 69bc1f81..40eee21d 100644 --- a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php +++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php @@ -1,143 +1,143 @@ - - * @link http://www.pradosoft.com/ - * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - */ - -/** - * Import scaffold base, list, edit and search controls. - */ -Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); -Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView'); -Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView'); -Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch'); - -/** - * TScaffoldView is a composite control consisting of TScaffoldListView - * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView - * when an "edit" command is raised from the TScaffoldListView (when the - * edit button is clicked). Futher more, the "add" button can be clicked - * that shows an empty data TScaffoldListView for creating new records. - * - * The {@link getListView ListView} property gives a TScaffoldListView for - * display the record data. The {@link getEditView EditView} is the - * TScaffoldEditView that renders the - * inputs for editing and adding records. The {@link getSearchControl SearchControl} - * is a TScaffoldSearch responsible to the search user interface. - * - * Set the {@link setRecordClass RecordClass} property to the name of - * the Active Record class to be displayed/edited/added. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord.Scaffold - * @since 3.0 - */ -class TScaffoldView extends TScaffoldBase -{ - /** - * Copy basic record details to the list/edit/search controls. - */ - public function onPreRender($param) - { - parent::onPreRender($param); - $this->getListView()->copyFrom($this); - $this->getEditView()->copyFrom($this); - $this->getSearchControl()->copyFrom($this); - } - - /** - * @return TScaffoldListView scaffold list view. - */ - public function getListView() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_listView'); - } - - /** - * @return TScaffoldEditView scaffold edit view. - */ - public function getEditView() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_editView'); - } - - /** - * @return TScaffoldSearch scaffold search textbox and button. - */ - public function getSearchControl() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_search'); - } - - /** - * @return TButton "Add new record" button. - */ - public function getAddButton() - { - $this->ensureChildControls(); - return $this->getRegisteredObject('_newButton'); - } - - /** - * Handle the "edit" and "new" commands by displaying the edit view. - * Default command shows the list view. - */ - public function bubbleEvent($sender,$param) - { - switch(strtolower($param->getCommandName())) - { - case 'edit': - return $this->showEditView($sender, $param); - case 'new': - return $this->showAddView($sender, $param); - default: - return $this->showListView($sender, $param); - } - return false; - } - - /** - * Shows the edit record view. - */ - protected function showEditView($sender, $param) - { - $this->getListView()->setVisible(false); - $this->getEditView()->setVisible(true); - $this->_panForNewButton->setVisible(false); - $this->_panForSearch->setVisible(false); - $this->getEditView()->getCancelButton()->setVisible(true); - $this->getEditView()->getClearButton()->setVisible(false); - } - - /** - * Shows the view for listing the records. - */ - protected function showListView($sender, $param) - { - $this->getListView()->setVisible(true); - $this->getEditView()->setVisible(false); - $this->_panForNewButton->setVisible(true); - $this->_panForSearch->setVisible(true); - } - - /** - * Shows the add record view. - */ - protected function showAddView($sender, $param) - { - $this->getEditView()->setRecordPk(null); - $this->getEditView()->initializeEditForm(); - $this->showEditView($sender, $param); - } -} - + + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2005-2012 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + */ + +/** + * Import scaffold base, list, edit and search controls. + */ +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView'); +Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch'); + +/** + * TScaffoldView is a composite control consisting of TScaffoldListView + * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView + * when an "edit" command is raised from the TScaffoldListView (when the + * edit button is clicked). Futher more, the "add" button can be clicked + * that shows an empty data TScaffoldListView for creating new records. + * + * The {@link getListView ListView} property gives a TScaffoldListView for + * display the record data. The {@link getEditView EditView} is the + * TScaffoldEditView that renders the + * inputs for editing and adding records. The {@link getSearchControl SearchControl} + * is a TScaffoldSearch responsible to the search user interface. + * + * Set the {@link setRecordClass RecordClass} property to the name of + * the Active Record class to be displayed/edited/added. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord.Scaffold + * @since 3.0 + */ +class TScaffoldView extends TScaffoldBase +{ + /** + * Copy basic record details to the list/edit/search controls. + */ + public function onPreRender($param) + { + parent::onPreRender($param); + $this->getListView()->copyFrom($this); + $this->getEditView()->copyFrom($this); + $this->getSearchControl()->copyFrom($this); + } + + /** + * @return TScaffoldListView scaffold list view. + */ + public function getListView() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_listView'); + } + + /** + * @return TScaffoldEditView scaffold edit view. + */ + public function getEditView() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_editView'); + } + + /** + * @return TScaffoldSearch scaffold search textbox and button. + */ + public function getSearchControl() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_search'); + } + + /** + * @return TButton "Add new record" button. + */ + public function getAddButton() + { + $this->ensureChildControls(); + return $this->getRegisteredObject('_newButton'); + } + + /** + * Handle the "edit" and "new" commands by displaying the edit view. + * Default command shows the list view. + */ + public function bubbleEvent($sender,$param) + { + switch(strtolower($param->getCommandName())) + { + case 'edit': + return $this->showEditView($sender, $param); + case 'new': + return $this->showAddView($sender, $param); + default: + return $this->showListView($sender, $param); + } + return false; + } + + /** + * Shows the edit record view. + */ + protected function showEditView($sender, $param) + { + $this->getListView()->setVisible(false); + $this->getEditView()->setVisible(true); + $this->_panForNewButton->setVisible(false); + $this->_panForSearch->setVisible(false); + $this->getEditView()->getCancelButton()->setVisible(true); + $this->getEditView()->getClearButton()->setVisible(false); + } + + /** + * Shows the view for listing the records. + */ + protected function showListView($sender, $param) + { + $this->getListView()->setVisible(true); + $this->getEditView()->setVisible(false); + $this->_panForNewButton->setVisible(true); + $this->_panForSearch->setVisible(true); + } + + /** + * Shows the add record view. + */ + protected function showAddView($sender, $param) + { + $this->getEditView()->setRecordPk(null); + $this->getEditView()->initializeEditForm(); + $this->showEditView($sender, $param); + } +} + diff --git a/framework/Data/ActiveRecord/TActiveRecordConfig.php b/framework/Data/ActiveRecord/TActiveRecordConfig.php index 478786d3..821c4223 100644 --- a/framework/Data/ActiveRecord/TActiveRecordConfig.php +++ b/framework/Data/ActiveRecord/TActiveRecordConfig.php @@ -1,201 +1,201 @@ - - * @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 - */ - -Prado::using('System.Data.TDataSourceConfig'); -Prado::using('System.Data.ActiveRecord.TActiveRecordManager'); - -/** - * TActiveRecordConfig module configuration class. - * - * Database configuration for the default ActiveRecord manager instance. - * - * Example: application.xml configuration - * - * - * - * - * - * - * - * - * MySQL database definition: - * - * CREATE TABLE `blogs` ( - * `blog_id` int(10) unsigned NOT NULL auto_increment, - * `blog_name` varchar(255) NOT NULL, - * `blog_author` varchar(255) NOT NULL, - * PRIMARY KEY (`blog_id`) - * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; - * - * - * Record php class: - * - * class Blogs extends TActiveRecord - * { - * public $blog_id; - * public $blog_name; - * public $blog_author; - * - * public static function finder($className=__CLASS__) - * { - * return parent::finder($className); - * } - * } - * - * - * Usage example: - * - * class Home extends TPage - * { - * function onLoad($param) - * { - * $blogs = Blogs::finder()->findAll(); - * print_r($blogs); - * } - * } - * - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord - * @since 3.1 - */ -class TActiveRecordConfig extends TDataSourceConfig -{ - const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager'; - const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; - - /** - * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} - * @var string - */ - private $_managerClass = self::DEFAULT_MANAGER_CLASS; - - /** - * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} - * @var string - */ - private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; - - /** - * @var TActiveRecordManager - */ - private $_manager = null; - - private $_enableCache=false; - - /** - * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' - * - * @var TActiveRecordInvalidFinderResult - * @since 3.1.5 - */ - private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; - - /** - * Initialize the active record manager. - * @param TXmlDocument xml configuration. - */ - public function init($xml) - { - parent::init($xml); - $manager = $this -> getManager(); - if($this->getEnableCache()) - $manager->setCache($this->getApplication()->getCache()); - $manager->setDbConnection($this->getDbConnection()); - $manager->setInvalidFinderResult($this->getInvalidFinderResult()); - $manager->setGatewayClass($this->getGatewayClass()); - } - - /** - * @return TActiveRecordManager - */ - public function getManager() { - if($this->_manager === null) - $this->_manager = Prado::createComponent($this -> getManagerClass()); - return TActiveRecordManager::getInstance($this->_manager); - } - - /** - * Set implementation class of ActiveRecordManager - * @param string $value - */ - public function setManagerClass($value) - { - $this->_managerClass = TPropertyValue::ensureString($value); - } - - /** - * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} - */ - public function getManagerClass() - { - return $this->_managerClass; - } - - /** - * Set implementation class of ActiveRecordGateway - * @param string $value - */ - public function setGatewayClass($value) - { - $this->_gatewayClass = TPropertyValue::ensureString($value); - } - - /** - * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} - */ - public function getGatewayClass() - { - return $this->_gatewayClass; - } - - /** - * Set true to cache the table meta data. - * @param boolean true to cache sqlmap instance. - */ - public function setEnableCache($value) - { - $this->_enableCache = TPropertyValue::ensureBoolean($value); - } - - /** - * @return boolean true if table meta data should be cached, false otherwise. - */ - public function getEnableCache() - { - return $this->_enableCache; - } - - /** - * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. - * @see setInvalidFinderResult - * @since 3.1.5 - */ - public function getInvalidFinderResult() - { - return $this->_invalidFinderResult; - } - - /** - * Define the way an active record finder react if an invalid magic-finder invoked - * - * @param TActiveRecordInvalidFinderResult - * @see getInvalidFinderResult - * @since 3.1.5 - */ - public function setInvalidFinderResult($value) - { - $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); - } -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.TDataSourceConfig'); +Prado::using('System.Data.ActiveRecord.TActiveRecordManager'); + +/** + * TActiveRecordConfig module configuration class. + * + * Database configuration for the default ActiveRecord manager instance. + * + * Example: application.xml configuration + * + * + * + * + * + * + * + * + * MySQL database definition: + * + * CREATE TABLE `blogs` ( + * `blog_id` int(10) unsigned NOT NULL auto_increment, + * `blog_name` varchar(255) NOT NULL, + * `blog_author` varchar(255) NOT NULL, + * PRIMARY KEY (`blog_id`) + * ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + * + * + * Record php class: + * + * class Blogs extends TActiveRecord + * { + * public $blog_id; + * public $blog_name; + * public $blog_author; + * + * public static function finder($className=__CLASS__) + * { + * return parent::finder($className); + * } + * } + * + * + * Usage example: + * + * class Home extends TPage + * { + * function onLoad($param) + * { + * $blogs = Blogs::finder()->findAll(); + * print_r($blogs); + * } + * } + * + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordConfig extends TDataSourceConfig +{ + const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager'; + const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; + + /** + * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} + * @var string + */ + private $_managerClass = self::DEFAULT_MANAGER_CLASS; + + /** + * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + * @var string + */ + private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; + + /** + * @var TActiveRecordManager + */ + private $_manager = null; + + private $_enableCache=false; + + /** + * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; + + /** + * Initialize the active record manager. + * @param TXmlDocument xml configuration. + */ + public function init($xml) + { + parent::init($xml); + $manager = $this -> getManager(); + if($this->getEnableCache()) + $manager->setCache($this->getApplication()->getCache()); + $manager->setDbConnection($this->getDbConnection()); + $manager->setInvalidFinderResult($this->getInvalidFinderResult()); + $manager->setGatewayClass($this->getGatewayClass()); + } + + /** + * @return TActiveRecordManager + */ + public function getManager() { + if($this->_manager === null) + $this->_manager = Prado::createComponent($this -> getManagerClass()); + return TActiveRecordManager::getInstance($this->_manager); + } + + /** + * Set implementation class of ActiveRecordManager + * @param string $value + */ + public function setManagerClass($value) + { + $this->_managerClass = TPropertyValue::ensureString($value); + } + + /** + * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS} + */ + public function getManagerClass() + { + return $this->_managerClass; + } + + /** + * Set implementation class of ActiveRecordGateway + * @param string $value + */ + public function setGatewayClass($value) + { + $this->_gatewayClass = TPropertyValue::ensureString($value); + } + + /** + * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + */ + public function getGatewayClass() + { + return $this->_gatewayClass; + } + + /** + * Set true to cache the table meta data. + * @param boolean true to cache sqlmap instance. + */ + public function setEnableCache($value) + { + $this->_enableCache = TPropertyValue::ensureBoolean($value); + } + + /** + * @return boolean true if table meta data should be cached, false otherwise. + */ + public function getEnableCache() + { + return $this->_enableCache; + } + + /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @see setInvalidFinderResult + * @since 3.1.5 + */ + public function getInvalidFinderResult() + { + return $this->_invalidFinderResult; + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * + * @param TActiveRecordInvalidFinderResult + * @see getInvalidFinderResult + * @since 3.1.5 + */ + public function setInvalidFinderResult($value) + { + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } +} diff --git a/framework/Data/ActiveRecord/TActiveRecordCriteria.php b/framework/Data/ActiveRecord/TActiveRecordCriteria.php index d15f83ee..cdd7f964 100644 --- a/framework/Data/ActiveRecord/TActiveRecordCriteria.php +++ b/framework/Data/ActiveRecord/TActiveRecordCriteria.php @@ -1,39 +1,39 @@ - * @link http://www.pradosoft.com/ +/** + * TActiveRecordCriteria class file. + * + * @author Wei Zhuo + * @link http://www.pradosoft.com/ * @copyright Copyright © 2005-2012 PradoSoft - * @license http://www.pradosoft.com/license/ - * @version $Id$ - * @package System.Data.ActiveRecord - */ - -Prado::using('System.Data.DataGateway.TSqlCriteria'); - -/** - * Search criteria for Active Record. - * - * Criteria object for active record finder methods. Usage: - * - * $criteria = new TActiveRecordCriteria; - * $criteria->Condition = 'username = :name AND password = :pass'; - * $criteria->Parameters[':name'] = 'admin'; - * $criteria->Parameters[':pass'] = 'prado'; - * $criteria->OrdersBy['level'] = 'desc'; - * $criteria->OrdersBy['name'] = 'asc'; - * $criteria->Limit = 10; - * $criteria->Offset = 20; - * - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord - * @since 3.1 - */ -class TActiveRecordCriteria extends TSqlCriteria -{ - -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.DataGateway.TSqlCriteria'); + +/** + * Search criteria for Active Record. + * + * Criteria object for active record finder methods. Usage: + * + * $criteria = new TActiveRecordCriteria; + * $criteria->Condition = 'username = :name AND password = :pass'; + * $criteria->Parameters[':name'] = 'admin'; + * $criteria->Parameters[':pass'] = 'prado'; + * $criteria->OrdersBy['level'] = 'desc'; + * $criteria->OrdersBy['name'] = 'asc'; + * $criteria->Limit = 10; + * $criteria->Offset = 20; + * + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordCriteria extends TSqlCriteria +{ + +} diff --git a/framework/Data/ActiveRecord/TActiveRecordManager.php b/framework/Data/ActiveRecord/TActiveRecordManager.php index 00979d1c..6cca76e7 100644 --- a/framework/Data/ActiveRecord/TActiveRecordManager.php +++ b/framework/Data/ActiveRecord/TActiveRecordManager.php @@ -1,163 +1,163 @@ - - * @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 - */ - -Prado::using('System.Data.TDbConnection'); -Prado::using('System.Data.ActiveRecord.TActiveRecord'); -Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException'); -Prado::using('System.Data.ActiveRecord.TActiveRecordGateway'); - -/** - * TActiveRecordManager provides the default DB connection, - * default active record gateway, and table meta data inspector. - * - * The default connection can be set as follows: - * - * TActiveRecordManager::getInstance()->setDbConnection($conn); - * - * All new active record created after setting the - * {@link DbConnection setDbConnection()} will use that connection unless - * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection(). - * - * Set the {@link setCache Cache} property to an ICache object to allow - * the active record gateway to cache the table meta data information. - * - * @author Wei Zhuo - * @version $Id$ - * @package System.Data.ActiveRecord - * @since 3.1 - */ -class TActiveRecordManager extends TComponent -{ - const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; - - /** - * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} - * @var string - */ - private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; - - private $_gateway; - private $_meta=array(); - private $_connection; - - private $_cache; - - /** - * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' - * - * @var TActiveRecordInvalidFinderResult - * @since 3.1.5 - */ - private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; - - /** - * @return ICache application cache. - */ - public function getCache() - { - return $this->_cache; - } - - /** - * @param ICache application cache - */ - public function setCache($value) - { - $this->_cache=$value; - } - - /** - * @param TDbConnection default database connection - */ - public function setDbConnection($conn) - { - $this->_connection=$conn; - } - - /** - * @return TDbConnection default database connection - */ - public function getDbConnection() - { - return $this->_connection; - } - - /** - * @return TActiveRecordManager static instance of record manager. - */ - public static function getInstance($self=null) - { - static $instance; - if($self!==null) - $instance=$self; - else if($instance===null) - $instance = new self; - return $instance; - } - - /** - * @return TActiveRecordGateway record gateway. - */ - public function getRecordGateway() - { - if($this->_gateway === null) { - $this->_gateway = $this->createRecordGateway(); - } - return $this->_gateway; - } - - /** - * @return TActiveRecordGateway default record gateway. - */ - protected function createRecordGateway() - { - return Prado::createComponent($this->getGatewayClass(), $this); - } - - /** - * Set implementation class of ActiveRecordGateway - * @param string $value - */ - public function setGatewayClass($value) - { - $this->_gatewayClass = (string)$value; - } - - /** - * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} - */ - public function getGatewayClass() - { - return $this->_gatewayClass; - } - - /** - * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. - * @since 3.1.5 - * @see setInvalidFinderResult - */ - public function getInvalidFinderResult() - { - return $this->_invalidFinderResult; - } - - /** - * Define the way an active record finder react if an invalid magic-finder invoked - * @param TActiveRecordInvalidFinderResult - * @since 3.1.5 - * @see getInvalidFinderResult - */ - public function setInvalidFinderResult($value) - { - $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); - } -} + * @license http://www.pradosoft.com/license/ + * @version $Id$ + * @package System.Data.ActiveRecord + */ + +Prado::using('System.Data.TDbConnection'); +Prado::using('System.Data.ActiveRecord.TActiveRecord'); +Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException'); +Prado::using('System.Data.ActiveRecord.TActiveRecordGateway'); + +/** + * TActiveRecordManager provides the default DB connection, + * default active record gateway, and table meta data inspector. + * + * The default connection can be set as follows: + * + * TActiveRecordManager::getInstance()->setDbConnection($conn); + * + * All new active record created after setting the + * {@link DbConnection setDbConnection()} will use that connection unless + * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection(). + * + * Set the {@link setCache Cache} property to an ICache object to allow + * the active record gateway to cache the table meta data information. + * + * @author Wei Zhuo + * @version $Id$ + * @package System.Data.ActiveRecord + * @since 3.1 + */ +class TActiveRecordManager extends TComponent +{ + const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway'; + + /** + * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + * @var string + */ + private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS; + + private $_gateway; + private $_meta=array(); + private $_connection; + + private $_cache; + + /** + * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}' + * + * @var TActiveRecordInvalidFinderResult + * @since 3.1.5 + */ + private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null; + + /** + * @return ICache application cache. + */ + public function getCache() + { + return $this->_cache; + } + + /** + * @param ICache application cache + */ + public function setCache($value) + { + $this->_cache=$value; + } + + /** + * @param TDbConnection default database connection + */ + public function setDbConnection($conn) + { + $this->_connection=$conn; + } + + /** + * @return TDbConnection default database connection + */ + public function getDbConnection() + { + return $this->_connection; + } + + /** + * @return TActiveRecordManager static instance of record manager. + */ + public static function getInstance($self=null) + { + static $instance; + if($self!==null) + $instance=$self; + else if($instance===null) + $instance = new self; + return $instance; + } + + /** + * @return TActiveRecordGateway record gateway. + */ + public function getRecordGateway() + { + if($this->_gateway === null) { + $this->_gateway = $this->createRecordGateway(); + } + return $this->_gateway; + } + + /** + * @return TActiveRecordGateway default record gateway. + */ + protected function createRecordGateway() + { + return Prado::createComponent($this->getGatewayClass(), $this); + } + + /** + * Set implementation class of ActiveRecordGateway + * @param string $value + */ + public function setGatewayClass($value) + { + $this->_gatewayClass = (string)$value; + } + + /** + * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS} + */ + public function getGatewayClass() + { + return $this->_gatewayClass; + } + + /** + * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'. + * @since 3.1.5 + * @see setInvalidFinderResult + */ + public function getInvalidFinderResult() + { + return $this->_invalidFinderResult; + } + + /** + * Define the way an active record finder react if an invalid magic-finder invoked + * @param TActiveRecordInvalidFinderResult + * @since 3.1.5 + * @see getInvalidFinderResult + */ + public function setInvalidFinderResult($value) + { + $this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult'); + } +} -- cgit v1.2.3