. */ #namespace Doctrine::ORM::Internal; /** * The metadata factory is used to create ClassMetadata objects that contain all the * metadata mapping informations of a class. * * @author Konsta Vesterinen * @author Roman Borschel * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @version $Revision$ * @link www.phpdoctrine.org * @since 2.0 * @todo Rename to ClassMetadataFactory. */ class Doctrine_ClassMetadata_Factory { protected $_em; protected $_driver; /** * The already loaded metadata objects. */ protected $_loadedMetadata = array(); /** * Constructor. * Creates a new factory instance that uses the given EntityManager and metadata driver * implementations. * * @param $conn The connection to use. * @param $driver The metadata driver to use. */ public function __construct(Doctrine_EntityManager $em, $driver) { $this->_em = $em; $this->_driver = $driver; } /** * Returns the metadata object for a class. * * @param string $className The name of the class. * @return Doctrine_Metadata */ public function getMetadataFor($className) { if (isset($this->_loadedMetadata[$className])) { return $this->_loadedMetadata[$className]; } $this->_loadClasses($className, $this->_loadedMetadata); return $this->_loadedMetadata[$className]; } /** * Loads the metadata of the class in question and all it's ancestors whose metadata * is still not loaded. * * @param string $name The name of the class for which the metadata should get loaded. * @param array $tables The metadata collection to which the loaded metadata is added. */ protected function _loadClasses($name, array &$classes) { $parentClass = $name; $parentClasses = array(); $loadedParentClass = false; while ($parentClass = get_parent_class($parentClass)) { if ($parentClass == 'Doctrine_Entity') { break; } if (isset($classes[$parentClass])) { $loadedParentClass = $parentClass; break; } $parentClasses[] = $parentClass; } $parentClasses = array_reverse($parentClasses); $parentClasses[] = $name; if ($loadedParentClass) { $class = $classes[$loadedParentClass]; } else { $rootClassOfHierarchy = count($parentClasses) > 0 ? array_shift($parentClasses) : $name; $class = new Doctrine_ClassMetadata($rootClassOfHierarchy, $this->_em); $this->_loadMetadata($class, $rootClassOfHierarchy); $classes[$rootClassOfHierarchy] = $class; } if (count($parentClasses) == 0) { return $class; } // load metadata of subclasses // -> child1 -> child2 -> $name $parent = $class; foreach ($parentClasses as $subclassName) { $subClass = new Doctrine_ClassMetadata($subclassName, $this->_em); $subClass->setInheritanceType($parent->getInheritanceType(), $parent->getInheritanceOptions()); $this->_addInheritedFields($subClass, $parent); $this->_addInheritedRelations($subClass, $parent); $this->_loadMetadata($subClass, $subclassName); if ($parent->getInheritanceType() == Doctrine::INHERITANCE_TYPE_SINGLE_TABLE) { $subClass->setTableName($parent->getTableName()); } $classes[$subclassName] = $subClass; $parent = $subClass; } } /** * Adds inherited fields to the subclass mapping. * * @param unknown_type $subClass * @param unknown_type $parentClass */ protected function _addInheritedFields($subClass, $parentClass) { foreach ($parentClass->getFieldMappings() as $fieldName => $mapping) { $fullName = "$name as " . $parentClass->getFieldName($name); $mapping['inherited'] = true; $subClass->mapField($mapping); } } /** * Adds inherited associations to the subclass mapping. * * @param unknown_type $subClass * @param unknown_type $parentClass */ protected function _addInheritedRelations($subClass, $parentClass) { foreach ($parentClass->getRelationParser()->getRelationDefinitions() as $name => $definition) { $subClass->getRelationParser()->addRelationDefinition($name, $definition); } } /** * Loads the metadata of a specified class. * * @param Doctrine_ClassMetadata $class The container for the metadata. * @param string $name The name of the class for which the metadata will be loaded. */ protected function _loadMetadata(Doctrine_ClassMetadata $class, $name) { if ( ! class_exists($name) || empty($name)) { throw new Doctrine_Exception("Couldn't find class " . $name . "."); } $names = array(); $className = $name; // get parent classes //TODO: Skip Entity types MappedSuperclass/Transient do { if ($className === 'Doctrine_Entity') { break; } else if ($className == $name) { continue; } $names[] = $className; } while ($className = get_parent_class($className)); if ($className === false) { throw new Doctrine_ClassMetadata_Factory_Exception("Unknown component '$className'."); } // save parents $class->setParentClasses($names); // load user-specified mapping metadata through the driver $this->_driver->loadMetadataForClass($name, $class); // set default table name, if necessary $tableName = $class->getTableName(); if ( ! isset($tableName)) { $class->setTableName(Doctrine::tableize($class->getClassName())); } $class->completeIdentifierMapping(); return $class; } }