diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index bf802ecf9..b327c5073 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -164,13 +164,11 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface } if ($this->cacheDriver) { - if (($cached = $this->cacheDriver->fetch("$realClassName\$CLASSMETADATA")) !== false) { + if (($cached = $this->fetchMetadataFromCache($realClassName)) !== false) { $this->loadedMetadata[$realClassName] = $cached; } else { foreach ($this->loadMetadata($realClassName) as $loadedClassName) { - $this->cacheDriver->save( - "$loadedClassName\$CLASSMETADATA", $this->loadedMetadata[$loadedClassName], null - ); + $this->cacheMetadata($loadedClassName, $this->loadedMetadata[$loadedClassName]); } } } else { @@ -319,6 +317,11 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface $class->setParentClasses($visited); + // Calculate Discriminator Map if needed and if no discriminator map is set + if ($class->isInheritanceTypeJoined() && empty($class->discriminatorMap)) { + $this->addDefaultDiscriminatorMap($class); + } + if ($this->evm->hasListeners(Events::loadClassMetadata)) { $eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em); $this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs); @@ -388,6 +391,89 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface return new ClassMetadata($className); } + /** + * Adds a default discriminator map if no one is given + * + * @param \Doctrine\ORM\Mapping\ClassMetadata $class + */ + private function addDefaultDiscriminatorMap(ClassMetadata $class) + { + $allClasses = $this->driver->getAllClassNames(); + $subClassesMetadata = array(); + $fqcn = $class->getName(); + $map = array(str_replace('\\', '.', $fqcn) => $fqcn); + + foreach ($allClasses as $c) { + if (is_subclass_of($c, $fqcn)) { + if (isset($this->loadedMetadata[$c])) { + $subClassMetadata = $this->loadedMetadata[$c]; + } else { + $subClassMetadata = $this->newClassMetadataInstance($c); + $this->driver->loadMetadataForClass($c, $subClassMetadata); + } + + if (!$subClassMetadata->isMappedSuperclass) { + $map[str_replace('\\', '.', $c)] = $c; + $subClassesMetadata[$c] = $subClassMetadata; + } + } + } + + $class->setDiscriminatorMap($map); + + // Now we set the discriminator map for the subclasses already loaded + foreach ($subClassesMetadata as $subClassFqcn => $subClassMetadata) { + $subClassMetadata->setDiscriminatorMap($map); + + // We need to overwrite the cached version of the metadata, because + // it was cached without the discriminator map + if ($this->cacheDriver && $this->cacheContainsMetadata($subClassFqcn)) { + // If subclass metadata is not already loaded, it's incomplete so + // we reload it again from cache + if (!isset($this->loadedMetadata[$subClassFqcn])) { + $subClassMetadata = $this->fetchMetadataFromCache($subClassFqcn); + $subClassMetadata->setDiscriminatorMap($map); + } + + $this->cacheMetadata($subClassFqcn, $subClassMetadata); + } + } + } + + /** + * Cache the metadata + * + * @param $className + * @param \Doctrine\ORM\Mapping\ClassMetadata $metadata + */ + private function cacheMetadata($className, ClassMetadata $metadata) + { + $this->cacheDriver->save( + "$className\$CLASSMETADATA", $metadata, null + ); + } + + /** + * Verify if metadata is cached + * + * @param $className + * @return bool + */ + private function cacheContainsMetadata($className) + { + return $this->cacheDriver->contains("$className\$CLASSMETADATA"); + } + + /** + * Fetch metadata from cache + * + * @param $className + */ + private function fetchMetadataFromCache($className) + { + return $this->cacheDriver->fetch("$className\$CLASSMETADATA"); + } + /** * Adds inherited fields to the subclass mapping. * diff --git a/tests/Doctrine/Tests/Models/JoinedInheritanceType/AnotherChildClass.php b/tests/Doctrine/Tests/Models/JoinedInheritanceType/AnotherChildClass.php new file mode 100644 index 000000000..e49d89807 --- /dev/null +++ b/tests/Doctrine/Tests/Models/JoinedInheritanceType/AnotherChildClass.php @@ -0,0 +1,10 @@ +assertTrue($em->getMetadataFactory()->isTransient('CMS:CmsUser')); $this->assertFalse($em->getMetadataFactory()->isTransient('CMS:CmsArticle')); } + + public function testAddDefaultDiscriminatorMap() + { + $cmf = new ClassMetadataFactory(); + $driver = $this->createAnnotationDriver(array(__DIR__ . '/../../Models/JoinedInheritanceType/')); + $em = $this->_createEntityManager($driver); + $cmf->setEntityManager($em); + + $rootMetadata = $cmf->getMetadataFor('Doctrine\Tests\Models\JoinedInheritanceType\RootClass'); + $childMetadata = $cmf->getMetadataFor('Doctrine\Tests\Models\JoinedInheritanceType\ChildClass'); + $anotherChildMetadata = $cmf->getMetadataFor('Doctrine\Tests\Models\JoinedInheritanceType\AnotherChildClass'); + $rootDiscriminatorMap = $rootMetadata->discriminatorMap; + $childDiscriminatorMap = $childMetadata->discriminatorMap; + $anotherChildDiscriminatorMap = $anotherChildMetadata->discriminatorMap; + + $rootClass = 'Doctrine\Tests\Models\JoinedInheritanceType\RootClass'; + $childClass = 'Doctrine\Tests\Models\JoinedInheritanceType\ChildClass'; + $anotherChildClass = 'Doctrine\Tests\Models\JoinedInheritanceType\AnotherChildClass'; + + $rootClassKey = array_search($rootClass, $rootDiscriminatorMap); + $childClassKey = array_search($childClass, $rootDiscriminatorMap); + $anotherChildClassKey = array_search($anotherChildClass, $rootDiscriminatorMap); + + $this->assertEquals(str_replace('\\', '.', $rootClass), $rootClassKey); + $this->assertFalse($childClassKey); + $this->assertEquals(str_replace('\\', '.', $anotherChildClassKey), $anotherChildClassKey); + + $this->assertEquals($childDiscriminatorMap, $rootDiscriminatorMap); + $this->assertEquals($anotherChildDiscriminatorMap, $rootDiscriminatorMap); + + // ClassMetadataFactory::addDefaultDiscriminatorMap shouldn't be called again, because the + // discriminator map is already cached + $cmf = $this->getMock('Doctrine\ORM\Mapping\ClassMetadataFactory', array('addDefaultDiscriminatorMap')); + $cmf->setEntityManager($em); + $cmf->expects($this->never()) + ->method('addDefaultDiscriminatorMap'); + + $rootMetadata = $cmf->getMetadataFor('Doctrine\Tests\Models\JoinedInheritanceType\RootClass'); + } protected function _createEntityManager($metadataDriver) {