Merge DDC-1542 into master
This commit is contained in:
commit
bd41e69a1f
@ -340,6 +340,10 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
|||||||
|
|
||||||
$class->setParentClasses($visited);
|
$class->setParentClasses($visited);
|
||||||
|
|
||||||
|
if ( $class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
|
||||||
|
$this->addDefaultDiscriminatorMap($class);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
|
if ($this->evm->hasListeners(Events::loadClassMetadata)) {
|
||||||
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em);
|
$eventArgs = new \Doctrine\ORM\Event\LoadClassMetadataEventArgs($class, $this->em);
|
||||||
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
$this->evm->dispatchEvent(Events::loadClassMetadata, $eventArgs);
|
||||||
@ -410,6 +414,95 @@ class ClassMetadataFactory implements ClassMetadataFactoryInterface
|
|||||||
return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy());
|
return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a default discriminator map if no one is given
|
||||||
|
*
|
||||||
|
* If an entity is of any inheritance type and does not contain a
|
||||||
|
* discrimiator map, then the map is generated automatically. This process
|
||||||
|
* is expensive computation wise.
|
||||||
|
*
|
||||||
|
* The automatically generated discriminator map contains the lowercase shortname of
|
||||||
|
* each class as key.
|
||||||
|
*
|
||||||
|
* @param \Doctrine\ORM\Mapping\ClassMetadata $class
|
||||||
|
*/
|
||||||
|
private function addDefaultDiscriminatorMap(ClassMetadata $class)
|
||||||
|
{
|
||||||
|
$allClasses = $this->driver->getAllClassNames();
|
||||||
|
$subClassesMetadata = array();
|
||||||
|
$fqcn = $class->getName();
|
||||||
|
$map = array($this->getShortName($class->name) => $fqcn);
|
||||||
|
|
||||||
|
$duplicates = array();
|
||||||
|
foreach ($allClasses as $subClassCandidate) {
|
||||||
|
if (is_subclass_of($subClassCandidate, $fqcn)) {
|
||||||
|
$shortName = $this->getShortName($subClassCandidate);
|
||||||
|
|
||||||
|
if (isset($map[$shortName])) {
|
||||||
|
$duplicates[] = $shortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
$map[$shortName] = $subClassCandidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($duplicates) {
|
||||||
|
throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $map);
|
||||||
|
}
|
||||||
|
|
||||||
|
$class->setDiscriminatorMap($map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the lower-case shortname of a class.
|
||||||
|
*
|
||||||
|
* @param string $className
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function getShortName($className)
|
||||||
|
{
|
||||||
|
if (strpos($className, "\\") === false) {
|
||||||
|
return strtolower($className);
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = explode("\\", $className);
|
||||||
|
return strtolower(end($parts));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Adds inherited fields to the subclass mapping.
|
||||||
*
|
*
|
||||||
|
@ -1898,6 +1898,16 @@ class ClassMetadataInfo implements ClassMetadata
|
|||||||
return isset($this->fieldMappings[$fieldName]['inherited']);
|
return isset($this->fieldMappings[$fieldName]['inherited']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this entity is the root in any entity-inheritance-hierachy.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isRootEntity()
|
||||||
|
{
|
||||||
|
return $this->name == $this->rootEntityName;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a mapped association field is inherited from a superclass.
|
* Checks whether a mapped association field is inherited from a superclass.
|
||||||
*
|
*
|
||||||
|
@ -298,6 +298,17 @@ class MappingException extends \Doctrine\ORM\ORMException
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function duplicateDiscriminatorEntry($className, array $entries, array $map)
|
||||||
|
{
|
||||||
|
return new self(
|
||||||
|
"The entries " . implode(', ', $entries) . " in discriminator map of class '" . $className . "' is duplicated. " .
|
||||||
|
"If the discriminator map is automatically generated you have to convert it to an explicit discriminator map now. " .
|
||||||
|
"The entries of the current map are: @DiscriminatorMap({" . implode(', ', array_map(
|
||||||
|
function($a, $b) { return "'$a': '$b'"; }, array_keys($map), array_values($map)
|
||||||
|
)) . "})"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public static function missingDiscriminatorMap($className)
|
public static function missingDiscriminatorMap($className)
|
||||||
{
|
{
|
||||||
return new self("Entity class '$className' is using inheritance but no discriminator map was defined.");
|
return new self("Entity class '$className' is using inheritance but no discriminator map was defined.");
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\Models\JoinedInheritanceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
*/
|
||||||
|
class AnotherChildClass extends ChildClass
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\Models\JoinedInheritanceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @MappedSuperclass
|
||||||
|
*/
|
||||||
|
abstract class ChildClass extends RootClass
|
||||||
|
{
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Doctrine\Tests\Models\JoinedInheritanceType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Entity
|
||||||
|
* @InheritanceType("JOINED")
|
||||||
|
*/
|
||||||
|
class RootClass
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @Column(type="integer")
|
||||||
|
* @Id @GeneratedValue
|
||||||
|
*/
|
||||||
|
public $id;
|
||||||
|
}
|
@ -153,6 +153,45 @@ class ClassMetadataFactoryTest extends \Doctrine\Tests\OrmTestCase
|
|||||||
$this->assertFalse($em->getMetadataFactory()->isTransient('CMS:CmsArticle'));
|
$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('rootclass', $rootClassKey);
|
||||||
|
$this->assertEquals('childclass', $childClassKey);
|
||||||
|
$this->assertEquals('anotherchildclass', $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)
|
protected function _createEntityManager($metadataDriver)
|
||||||
{
|
{
|
||||||
$driverMock = new DriverMock();
|
$driverMock = new DriverMock();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user