From 4ceba82b9863f2c6323cbe00407e4bfbedbfc1cd Mon Sep 17 00:00:00 2001 From: wei <> Date: Mon, 8 Oct 2007 03:24:07 +0000 Subject: Allow active records to have multiple foreign key references to the same table. Add TXCache. --- .../Relations/TActiveRecordBelongsTo.php | 13 +++- .../Relations/TActiveRecordHasMany.php | 17 +++- .../Relations/TActiveRecordHasManyAssociation.php | 24 +++--- .../ActiveRecord/Relations/TActiveRecordHasOne.php | 14 +++- .../Relations/TActiveRecordRelation.php | 60 ++++++++++++-- .../Relations/TActiveRecordRelationContext.php | 91 +++++++++++++++------- 6 files changed, 165 insertions(+), 54 deletions(-) (limited to 'framework/Data/ActiveRecord/Relations') diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php index d7278d64..d4ff07e5 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php @@ -77,8 +77,7 @@ class TActiveRecordBelongsTo extends TActiveRecordRelation */ protected function collectForeignObjects(&$results) { - $fkObject = $this->getContext()->getForeignRecordFinder(); - $fkeys = $this->findForeignKeys($this->getSourceRecord(),$fkObject); + $fkeys = $this->getRelationForeignKeys(); $properties = array_keys($fkeys); $fields = array_values($fkeys); @@ -87,6 +86,16 @@ class TActiveRecordBelongsTo extends TActiveRecordRelation $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. diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php index cbba3ebd..f7426862 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php @@ -1,4 +1,4 @@ -getContext()->getForeignRecordFinder(); - $fkeys = $this->findForeignKeys($fkObject, $this->getSourceRecord()); + $fkeys = $this->getRelationForeignKeys(); $properties = array_values($fkeys); $fields = array_keys($fkeys); @@ -86,6 +85,16 @@ class TActiveRecordHasMany extends TActiveRecordRelation $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 . @@ -113,5 +122,5 @@ class TActiveRecordHasMany extends TActiveRecordRelation return $success; } } - + ?> \ No newline at end of file diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php index 0e176607..564d3d22 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php @@ -37,7 +37,7 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); * * public static $RELATIONS = array * ( - * 'Categories' => array(self::HAS_MANY, 'CategoryRecord', 'Article_Category') + * 'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category') * ); * * public static function finder($className=__CLASS__) @@ -54,7 +54,7 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation'); * * public static $RELATIONS = array * ( - * 'Articles' => array(self::HAS_MANY, 'ArticleRecord', 'Article_Category') + * 'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category') * ); * * public static function finder($className=__CLASS__) @@ -96,17 +96,22 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation */ protected function collectForeignObjects(&$results) { - $association = $this->getAssociationTable(); - $sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord()); - + 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); - - $this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys); + return array($sourceKeys, $foreignKeys); } /** @@ -182,7 +187,7 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation */ protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys) { - $criteria = $this->getContext()->getCriteria(); + $criteria = $this->getCriteria(); $finder = $this->getContext()->getForeignRecordFinder(); $registry = $finder->getRecordManager()->getObjectStateRegistry(); $type = get_class($finder); @@ -198,7 +203,6 @@ class TActiveRecordHasManyAssociation extends TActiveRecordRelation $collections[$hash][] = $obj; $registry->registerClean($obj); } - $this->setResultCollection($results, $collections, array_values($sourceKeys)); } diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php index b22428ae..e5a36659 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php @@ -92,9 +92,7 @@ class TActiveRecordHasOne extends TActiveRecordRelation */ protected function collectForeignObjects(&$results) { - $fkObject = $this->getContext()->getForeignRecordFinder(); - $fkeys = $this->findForeignKeys($fkObject, $this->getSourceRecord()); - + $fkeys = $this->getRelationForeignKeys(); $properties = array_values($fkeys); $fields = array_keys($fkeys); @@ -102,6 +100,16 @@ class TActiveRecordHasOne extends TActiveRecordRelation $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. diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php index 5a3ea50e..8e9cc9b5 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php @@ -26,10 +26,12 @@ Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext'); abstract class TActiveRecordRelation { private $_context; + private $_criteria; - public function __construct(TActiveRecordRelationContext $context) + public function __construct(TActiveRecordRelationContext $context, $criteria) { $this->_context = $context; + $this->_criteria = $criteria; } /** @@ -39,6 +41,14 @@ abstract class TActiveRecordRelation { return $this->_context; } + + /** + * @return TActiveRecordCriteria + */ + protected function getCriteria() + { + return $this->_criteria; + } /** * @return TActiveRecord @@ -75,6 +85,16 @@ abstract class TActiveRecordRelation array_push($stack,$this); //call it later 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 @@ -84,7 +104,7 @@ abstract class TActiveRecordRelation * @param TActiveRecord $matchesRecord * @return array foreign keys with source column names as key and foreign column names as value. */ - protected function findForeignKeys($from, $matchesRecord) + protected function findForeignKeys($from, $matchesRecord, $loose=false) { $gateway = $matchesRecord->getRecordGateway(); $matchingTableName = $gateway->getRecordTableInfo($matchesRecord)->getTableName(); @@ -93,13 +113,42 @@ abstract class TActiveRecordRelation $tableInfo = $gateway->getRecordTableInfo($from); foreach($tableInfo->getForeignKeys() as $fkeys) { - if($fkeys['table']===$matchingTableName) - return $fkeys['keys']; + if(strtolower($fkeys['table'])===strtolower($matchingTableName)) + { + if(!$loose && $this->getContext()->hasFkField()) + return $this->getFkFields($fkeys['keys']); + else + return $fkeys['keys']; + } } $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 @@ -122,9 +171,8 @@ abstract class TActiveRecordRelation */ protected function findForeignObjects($fields, $indexValues) { - $criteria = $this->getContext()->getCriteria(); $finder = $this->getContext()->getForeignRecordFinder(); - return $finder->findAllByIndex($criteria, $fields, $indexValues); + return $finder->findAllByIndex($this->_criteria, $fields, $indexValues); } /** diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php index 189d2c5e..d83aa63a 100644 --- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php +++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php @@ -32,13 +32,12 @@ class TActiveRecordRelationContext private $_property; private $_sourceRecord; - private $_criteria; - private $_relation; + private $_relation; //data from an entry of TActiveRecord::$RELATION + private $_fkeys; - public function __construct($source, $property=null, $criteria=null) + public function __construct($source, $property=null) { $this->_sourceRecord=$source; - $this->_criteria=$criteria; if($property!==null) list($this->_property, $this->_relation) = $this->getSourceRecordRelation($property); } @@ -48,7 +47,6 @@ class TActiveRecordRelationContext * property from the $RELATIONS static property in TActiveRecord. * @param string relation property name * @return array array($propertyName, $relation) relation definition. - * @throws TActiveRecordException if property is not defined or missing. */ protected function getSourceRecordRelation($property) { @@ -58,8 +56,6 @@ class TActiveRecordRelationContext if(strtolower($name)===$property) return array($name, $relation); } - throw new TActiveRecordException('ar_undefined_relation_prop', - $property, get_class($this->_sourceRecord), self::RELATIONS_CONST); } /** @@ -71,6 +67,15 @@ class TActiveRecordRelationContext return $class->getStaticPropertyValue(self::RELATIONS_CONST); } + /** + * @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(); @@ -85,14 +90,6 @@ class TActiveRecordRelationContext return $this->_property; } - /** - * @return TActiveRecordCriteria sql query criteria for fetching the related record. - */ - public function getCriteria() - { - return $this->_criteria; - } - /** * @return TActiveRecord the active record instance that queried for its related records. */ @@ -109,6 +106,18 @@ class TActiveRecordRelationContext return $this->_relation[1]; } + /** + * @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 */ @@ -117,6 +126,25 @@ class TActiveRecordRelationContext return $this->_relation[0]; } + /** + * @return string foreign key field names, comma delimited. + * @since 3.1.2 + */ + public function getFkField() + { + return $this->_relation[2]; + } + + /** + * @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. */ @@ -130,7 +158,8 @@ class TActiveRecordRelationContext */ public function hasAssociationTable() { - return isset($this->_relation[2]); + $isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY; + return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]); } /** @@ -145,29 +174,33 @@ class TActiveRecordRelationContext * 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() + public function getRelationHandler($criteria=null) { + if(!$this->hasRecordRelation()) + { + throw new TActiveRecordException('ar_undefined_relation_prop', + $property, get_class($this->_sourceRecord), self::RELATIONS_CONST); + } + if($criteria===null) + $criteria = new TActiveRecordCriteria; switch($this->getRelationType()) { case TActiveRecord::HAS_MANY: - if(!$this->hasAssociationTable()) - { - Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany'); - return new TActiveRecordHasMany($this); - } - else - { - Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation'); - return new TActiveRecordHasManyAssociation($this); - } + 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); + return new TActiveRecordHasOne($this, $criteria); case TActiveRecord::BELONGS_TO: Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo'); - return new TActiveRecordBelongsTo($this); + return new TActiveRecordBelongsTo($this, $criteria); default: throw new TActiveRecordException('ar_invalid_relationship'); } -- cgit v1.2.3