1
0
mirror of synced 2025-02-21 14:43:14 +03:00

DDC-752 - Postpone Inheritance Related Metadata Validation into CMF

This commit is contained in:
Benjamin Eberlei 2010-08-27 22:14:48 +02:00
parent 797d9f1be5
commit d115f7af4f
5 changed files with 106 additions and 117 deletions

View File

@ -38,17 +38,40 @@ use ReflectionException,
*/ */
class ClassMetadataFactory class ClassMetadataFactory
{ {
private $_em; /**
/** The targeted database platform. */ * @var EntityManager
private $_targetPlatform; */
/** The used metadata driver. */ private $em;
private $_driver;
/** The event manager instance */ /**
private $_evm; * @var AbstractPlatform
/** The used cache driver. */ */
private $_cacheDriver; private $targetPlatform;
private $_loadedMetadata = array();
private $_initialized = false; /**
* @var Driver\Driver
*/
private $driver;
/**
* @var \Doctrine\Common\EventManager
*/
private $evm;
/**
* @var \Doctrine\Common\Cache\Cache
*/
private $cacheDriver;
/**
* @var array
*/
private $loadedMetadata = array();
/**
* @var bool
*/
private $initialized = false;
/** /**
* Creates a new factory instance that uses the given metadata driver implementation. * Creates a new factory instance that uses the given metadata driver implementation.
@ -57,7 +80,7 @@ class ClassMetadataFactory
*/ */
public function __construct(EntityManager $em) public function __construct(EntityManager $em)
{ {
$this->_em = $em; $this->em = $em;
} }
/** /**
@ -67,7 +90,7 @@ class ClassMetadataFactory
*/ */
public function setCacheDriver($cacheDriver) public function setCacheDriver($cacheDriver)
{ {
$this->_cacheDriver = $cacheDriver; $this->cacheDriver = $cacheDriver;
} }
/** /**
@ -77,12 +100,12 @@ class ClassMetadataFactory
*/ */
public function getCacheDriver() public function getCacheDriver()
{ {
return $this->_cacheDriver; return $this->cacheDriver;
} }
public function getLoadedMetadata() public function getLoadedMetadata()
{ {
return $this->_loadedMetadata; return $this->loadedMetadata;
} }
/** /**
@ -93,12 +116,12 @@ class ClassMetadataFactory
*/ */
public function getAllMetadata() public function getAllMetadata()
{ {
if ( ! $this->_initialized) { if ( ! $this->initialized) {
$this->_initialize(); $this->initialize();
} }
$metadata = array(); $metadata = array();
foreach ($this->_driver->getAllClassNames() as $className) { foreach ($this->driver->getAllClassNames() as $className) {
$metadata[] = $this->getMetadataFor($className); $metadata[] = $this->getMetadataFor($className);
} }
@ -109,12 +132,12 @@ class ClassMetadataFactory
* Lazy initialization of this stuff, especially the metadata driver, * Lazy initialization of this stuff, especially the metadata driver,
* since these are not needed at all when a metadata cache is active. * since these are not needed at all when a metadata cache is active.
*/ */
private function _initialize() private function initialize()
{ {
$this->_driver = $this->_em->getConfiguration()->getMetadataDriverImpl(); $this->driver = $this->em->getConfiguration()->getMetadataDriverImpl();
$this->_targetPlatform = $this->_em->getConnection()->getDatabasePlatform(); $this->targetPlatform = $this->em->getConnection()->getDatabasePlatform();
$this->_evm = $this->_em->getEventManager(); $this->evm = $this->em->getEventManager();
$this->_initialized = true; $this->initialized = true;
} }
/** /**
@ -125,43 +148,43 @@ class ClassMetadataFactory
*/ */
public function getMetadataFor($className) public function getMetadataFor($className)
{ {
if ( ! isset($this->_loadedMetadata[$className])) { if ( ! isset($this->loadedMetadata[$className])) {
$realClassName = $className; $realClassName = $className;
// Check for namespace alias // Check for namespace alias
if (strpos($className, ':') !== false) { if (strpos($className, ':') !== false) {
list($namespaceAlias, $simpleClassName) = explode(':', $className); list($namespaceAlias, $simpleClassName) = explode(':', $className);
$realClassName = $this->_em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName; $realClassName = $this->em->getConfiguration()->getEntityNamespace($namespaceAlias) . '\\' . $simpleClassName;
if (isset($this->_loadedMetadata[$realClassName])) { if (isset($this->loadedMetadata[$realClassName])) {
// We do not have the alias name in the map, include it // We do not have the alias name in the map, include it
$this->_loadedMetadata[$className] = $this->_loadedMetadata[$realClassName]; $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
return $this->_loadedMetadata[$realClassName]; return $this->loadedMetadata[$realClassName];
} }
} }
if ($this->_cacheDriver) { if ($this->cacheDriver) {
if (($cached = $this->_cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) { if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) {
$this->_loadedMetadata[$realClassName] = $cached; $this->loadedMetadata[$realClassName] = $cached;
} else { } else {
foreach ($this->_loadMetadata($realClassName) as $loadedClassName) { foreach ($this->loadMetadata($realClassName) as $loadedClassName) {
$this->_cacheDriver->save( $this->cacheDriver->save(
"$loadedClassName\$CLASSMETADATA", $this->_loadedMetadata[$loadedClassName], null "$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null
); );
} }
} }
} else { } else {
$this->_loadMetadata($realClassName); $this->loadMetadata($realClassName);
} }
if ($className != $realClassName) { if ($className != $realClassName) {
// We do not have the alias name in the map, include it // We do not have the alias name in the map, include it
$this->_loadedMetadata[$className] = $this->_loadedMetadata[$realClassName]; $this->loadedMetadata[$className] = $this->loadedMetadata[$realClassName];
} }
} }
return $this->_loadedMetadata[$className]; return $this->loadedMetadata[$className];
} }
/** /**
@ -172,7 +195,7 @@ class ClassMetadataFactory
*/ */
public function hasMetadataFor($className) public function hasMetadataFor($className)
{ {
return isset($this->_loadedMetadata[$className]); return isset($this->loadedMetadata[$className]);
} }
/** /**
@ -185,7 +208,7 @@ class ClassMetadataFactory
*/ */
public function setMetadataFor($className, $class) public function setMetadataFor($className, $class)
{ {
$this->_loadedMetadata[$className] = $class; $this->loadedMetadata[$className] = $class;
} }
/** /**
@ -194,12 +217,12 @@ class ClassMetadataFactory
* @param string $name * @param string $name
* @return array $parentClasses * @return array $parentClasses
*/ */
protected function _getParentClasses($name) protected function getParentClasses($name)
{ {
// Collect parent classes, ignoring transient (not-mapped) classes. // Collect parent classes, ignoring transient (not-mapped) classes.
$parentClasses = array(); $parentClasses = array();
foreach (array_reverse(class_parents($name)) as $parentClass) { foreach (array_reverse(class_parents($name)) as $parentClass) {
if ( ! $this->_driver->isTransient($parentClass)) { if ( ! $this->driver->isTransient($parentClass)) {
$parentClasses[] = $parentClass; $parentClasses[] = $parentClass;
} }
} }
@ -213,37 +236,37 @@ class ClassMetadataFactory
* @param string $name The name of the class for which the metadata should get 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. * @param array $tables The metadata collection to which the loaded metadata is added.
*/ */
protected function _loadMetadata($name) protected function loadMetadata($name)
{ {
if ( ! $this->_initialized) { if ( ! $this->initialized) {
$this->_initialize(); $this->initialize();
} }
$loaded = array(); $loaded = array();
$parentClasses = $this->_getParentClasses($name); $parentClasses = $this->getParentClasses($name);
$parentClasses[] = $name; $parentClasses[] = $name;
// Move down the hierarchy of parent classes, starting from the topmost class // Move down the hierarchy of parent classes, starting from the topmost class
$parent = null; $parent = null;
$visited = array(); $visited = array();
foreach ($parentClasses as $className) { foreach ($parentClasses as $className) {
if (isset($this->_loadedMetadata[$className])) { if (isset($this->loadedMetadata[$className])) {
$parent = $this->_loadedMetadata[$className]; $parent = $this->loadedMetadata[$className];
if ( ! $parent->isMappedSuperclass) { if ( ! $parent->isMappedSuperclass) {
array_unshift($visited, $className); array_unshift($visited, $className);
} }
continue; continue;
} }
$class = $this->_newClassMetadataInstance($className); $class = $this->newClassMetadataInstance($className);
if ($parent) { if ($parent) {
$class->setInheritanceType($parent->inheritanceType); $class->setInheritanceType($parent->inheritanceType);
$class->setDiscriminatorColumn($parent->discriminatorColumn); $class->setDiscriminatorColumn($parent->discriminatorColumn);
$class->setIdGeneratorType($parent->generatorType); $class->setIdGeneratorType($parent->generatorType);
$this->_addInheritedFields($class, $parent); $this->addInheritedFields($class, $parent);
$this->_addInheritedRelations($class, $parent); $this->addInheritedRelations($class, $parent);
$class->setIdentifier($parent->identifier); $class->setIdentifier($parent->identifier);
$class->setVersioned($parent->isVersioned); $class->setVersioned($parent->isVersioned);
$class->setVersionField($parent->versionField); $class->setVersionField($parent->versionField);
@ -254,7 +277,7 @@ class ClassMetadataFactory
// Invoke driver // Invoke driver
try { try {
$this->_driver->loadMetadataForClass($className, $class); $this->driver->loadMetadataForClass($className, $class);
} catch (ReflectionException $e) { } catch (ReflectionException $e) {
throw MappingException::reflectionFailure($className, $e); throw MappingException::reflectionFailure($className, $e);
} }
@ -276,7 +299,17 @@ class ClassMetadataFactory
$class->setIdGenerator($parent->idGenerator); $class->setIdGenerator($parent->idGenerator);
} }
} else { } else {
$this->_completeIdGeneratorMapping($class); $this->completeIdGeneratorMapping($class);
}
// verify inheritance
if (!$parent && !$class->isMappedSuperclass && !$class->isInheritanceTypeNone()) {
if (count($class->discriminatorMap) == 0) {
throw MappingException::missingDiscriminatorMap($class->name);
}
if (!$class->discriminatorColumn) {
throw MappingException::missingDiscriminatorColumn($class->name);
}
} }
if ($parent && $parent->isInheritanceTypeSingleTable()) { if ($parent && $parent->isInheritanceTypeSingleTable()) {
@ -285,12 +318,12 @@ class ClassMetadataFactory
$class->setParentClasses($visited); $class->setParentClasses($visited);
if ($this->_evm->hasListeners(Events::loadClassMetadata)) { if ($this->evm->hasListeners(Events::loadClassMetadata)) {
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class); $eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class);
$this->_evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
} }
$this->_loadedMetadata[$className] = $class; $this->loadedMetadata[$className] = $class;
$parent = $class; $parent = $class;
@ -310,7 +343,7 @@ class ClassMetadataFactory
* @param string $className * @param string $className
* @return Doctrine\ORM\Mapping\ClassMetadata * @return Doctrine\ORM\Mapping\ClassMetadata
*/ */
protected function _newClassMetadataInstance($className) protected function newClassMetadataInstance($className)
{ {
return new ClassMetadata($className); return new ClassMetadata($className);
} }
@ -321,7 +354,7 @@ class ClassMetadataFactory
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass * @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass * @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/ */
private function _addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass) private function addInheritedFields(ClassMetadata $subClass, ClassMetadata $parentClass)
{ {
foreach ($parentClass->fieldMappings as $fieldName => $mapping) { foreach ($parentClass->fieldMappings as $fieldName => $mapping) {
if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) { if ( ! isset($mapping['inherited']) && ! $parentClass->isMappedSuperclass) {
@ -343,7 +376,7 @@ class ClassMetadataFactory
* @param Doctrine\ORM\Mapping\ClassMetadata $subClass * @param Doctrine\ORM\Mapping\ClassMetadata $subClass
* @param Doctrine\ORM\Mapping\ClassMetadata $parentClass * @param Doctrine\ORM\Mapping\ClassMetadata $parentClass
*/ */
private function _addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass) private function addInheritedRelations(ClassMetadata $subClass, ClassMetadata $parentClass)
{ {
foreach ($parentClass->associationMappings as $field => $mapping) { foreach ($parentClass->associationMappings as $field => $mapping) {
//$subclassMapping = $mapping; //$subclassMapping = $mapping;
@ -363,13 +396,13 @@ class ClassMetadataFactory
* *
* @param Doctrine\ORM\Mapping\ClassMetadata $class * @param Doctrine\ORM\Mapping\ClassMetadata $class
*/ */
private function _completeIdGeneratorMapping(ClassMetadataInfo $class) private function completeIdGeneratorMapping(ClassMetadataInfo $class)
{ {
$idGenType = $class->generatorType; $idGenType = $class->generatorType;
if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) { if ($idGenType == ClassMetadata::GENERATOR_TYPE_AUTO) {
if ($this->_targetPlatform->prefersSequences()) { if ($this->targetPlatform->prefersSequences()) {
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE); $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_SEQUENCE);
} else if ($this->_targetPlatform->prefersIdentityColumns()) { } else if ($this->targetPlatform->prefersIdentityColumns()) {
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY); $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
} else { } else {
$class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE); $class->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_TABLE);
@ -382,7 +415,7 @@ class ClassMetadataFactory
// For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to // For PostgreSQL IDENTITY (SERIAL) we need a sequence name. It defaults to
// <table>_<column>_seq in PostgreSQL for SERIAL columns. // <table>_<column>_seq in PostgreSQL for SERIAL columns.
// Not pretty but necessary and the simplest solution that currently works. // Not pretty but necessary and the simplest solution that currently works.
$seqName = $this->_targetPlatform instanceof Platforms\PostgreSQLPlatform ? $seqName = $this->targetPlatform instanceof Platforms\PostgreSQLPlatform ?
$class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' : $class->table['name'] . '_' . $class->columnNames[$class->identifier[0]] . '_seq' :
null; null;
$class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName)); $class->setIdGenerator(new \Doctrine\ORM\Id\IdentityGenerator($seqName));
@ -392,7 +425,7 @@ class ClassMetadataFactory
$definition = $class->sequenceGeneratorDefinition; $definition = $class->sequenceGeneratorDefinition;
if ( ! $definition) { if ( ! $definition) {
$sequenceName = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq'; $sequenceName = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq';
$definition['sequenceName'] = $this->_targetPlatform->fixSchemaElementName($sequenceName); $definition['sequenceName'] = $this->targetPlatform->fixSchemaElementName($sequenceName);
$definition['allocationSize'] = 1; $definition['allocationSize'] = 1;
$definition['initialValue'] = 1; $definition['initialValue'] = 1;
$class->setSequenceGeneratorDefinition($definition); $class->setSequenceGeneratorDefinition($definition);

View File

@ -179,16 +179,12 @@ class AnnotationDriver implements Driver
'type' => $discrColumnAnnot->type, 'type' => $discrColumnAnnot->type,
'length' => $discrColumnAnnot->length 'length' => $discrColumnAnnot->length
)); ));
} else {
throw MappingException::missingDiscriminatorColumn($className);
} }
// Evaluate DiscriminatorMap annotation // Evaluate DiscriminatorMap annotation
if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) { if (isset($classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'])) {
$discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap']; $discrMapAnnot = $classAnnotations['Doctrine\ORM\Mapping\DiscriminatorMap'];
$metadata->setDiscriminatorMap($discrMapAnnot->value); $metadata->setDiscriminatorMap($discrMapAnnot->value);
} else {
throw MappingException::missingDiscriminatorMap($className);
} }
} }
} }

View File

@ -44,7 +44,7 @@ class DisconnectedClassMetadataFactory extends ClassMetadataFactory
/** /**
* @override * @override
*/ */
protected function _newClassMetadataInstance($className) protected function newClassMetadataInstance($className)
{ {
return new ClassMetadataInfo($className); return new ClassMetadataInfo($className);
} }
@ -52,7 +52,7 @@ class DisconnectedClassMetadataFactory extends ClassMetadataFactory
/** /**
* @override * @override
*/ */
protected function _getParentClasses($name) protected function getParentClasses($name)
{ {
return array(); return array();
} }

View File

@ -90,24 +90,6 @@ class AnnotationDriverTest extends AbstractMappingDriverTest
$this->assertNotContains($extraneousClassName, $classes); $this->assertNotContains($extraneousClassName, $classes);
} }
public function testInheritenceWithoutDiscriminatorMap()
{
$cm = new ClassMetadata('Doctrine\Tests\ORM\Mapping\ClassWithoutDiscriminatorMap');
$annotationDriver = $this->_loadDriver();
$this->setExpectedException("Doctrine\ORM\Mapping\MappingException");
$annotationDriver->loadMetadataForClass($cm->name, $cm);
}
public function testInheritenceWithoutDiscriminatorColumn()
{
$cm = new ClassMetadata('Doctrine\Tests\ORM\Mapping\ClassWithoutDiscriminatorColumn');
$annotationDriver = $this->_loadDriver();
$this->setExpectedException("Doctrine\ORM\Mapping\MappingException");
$annotationDriver->loadMetadataForClass($cm->name, $cm);
}
protected function _loadDriverForCMSModels() protected function _loadDriverForCMSModels()
{ {
$annotationDriver = $this->_loadDriver(); $annotationDriver = $this->_loadDriver();
@ -137,25 +119,3 @@ class ColumnWithoutType
/** @Id @Column */ /** @Id @Column */
public $id; public $id;
} }
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorMap({"a" = "ClassWithoutDiscriminatorColumn"})
*/
class ClassWithoutDiscriminatorColumn
{
/** @Id @Column */
public $id;
}
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @DiscriminatorColumn(name="discr", type="string")
*/
class ClassWithoutDiscriminatorMap
{
/** @Id @Column */
public $id;
}

View File

@ -102,27 +102,27 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
/* Test subject class with overriden factory method for mocking purposes */ /* Test subject class with overriden factory method for mocking purposes */
class ClassMetadataFactoryTestSubject extends \Doctrine\ORM\Mapping\ClassMetadataFactory class ClassMetadataFactoryTestSubject extends \Doctrine\ORM\Mapping\ClassMetadataFactory
{ {
private $_mockMetadata = array(); private $mockMetadata = array();
private $_requestedClasses = array(); private $requestedClasses = array();
/** @override */ /** @override */
protected function _newClassMetadataInstance($className) protected function newClassMetadataInstance($className)
{ {
$this->_requestedClasses[] = $className; $this->requestedClasses[] = $className;
if ( ! isset($this->_mockMetadata[$className])) { if ( ! isset($this->mockMetadata[$className])) {
throw new InvalidArgumentException("No mock metadata found for class $className."); throw new InvalidArgumentException("No mock metadata found for class $className.");
} }
return $this->_mockMetadata[$className]; return $this->mockMetadata[$className];
} }
public function setMetadataForClass($className, $metadata) public function setMetadataForClass($className, $metadata)
{ {
$this->_mockMetadata[$className] = $metadata; $this->mockMetadata[$className] = $metadata;
} }
public function getRequestedClasses() public function getRequestedClasses()
{ {
return $this->_requestedClasses; return $this->requestedClasses;
} }
} }