Skip to content

Commit

Permalink
[DDC-1542] Refactored automatic discriminator map detection.
Browse files Browse the repository at this point in the history
  • Loading branch information
beberlei committed May 4, 2012
1 parent f0db9a8 commit 99e303e
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 34 deletions.
65 changes: 35 additions & 30 deletions lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php
Expand Up @@ -317,8 +317,7 @@ protected function loadMetadata($name)

$class->setParentClasses($visited);

// Calculate Discriminator Map if needed and if no discriminator map is set
if ($class->isInheritanceTypeJoined() && empty($class->discriminatorMap)) {
if ( $class->isRootEntity() && ! $class->isInheritanceTypeNone() && ! $class->discriminatorMap) {
$this->addDefaultDiscriminatorMap($class);
}

Expand Down Expand Up @@ -394,50 +393,56 @@ protected function newClassMetadataInstance($className)
/**
* 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(str_replace('\\', '.', $fqcn) => $fqcn);
$map = array($this->getShortName($class->name) => $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);
}
$duplicates = array();
foreach ($allClasses as $subClassCandidate) {
if (is_subclass_of($subClassCandidate, $fqcn)) {
$shortName = $this->getShortName($subClassCandidate);

if (!$subClassMetadata->isMappedSuperclass) {
$map[str_replace('\\', '.', $c)] = $c;
$subClassesMetadata[$c] = $subClassMetadata;
if (isset($map[$shortName])) {
$duplicates[] = $shortName;

This comment has been minimized.

Copy link
@gquemener

gquemener Jun 27, 2013

Contributor

Is there any reason why it could/should not compute a new unique $shortName @beberlei ?

This comment has been minimized.

Copy link
@gquemener

gquemener Jun 27, 2013

Contributor

Or use the FQCN as the key?

}

$map[$shortName] = $subClassCandidate;
}
}

$class->setDiscriminatorMap($map);
if ($duplicates) {
throw MappingException::duplicateDiscriminatorEntry($class->name, $duplicates, $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);
}
$class->setDiscriminatorMap($map);
}

$this->cacheMetadata($subClassFqcn, $subClassMetadata);
}
/**
* 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));
}

/**
Expand Down
10 changes: 10 additions & 0 deletions lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php
Expand Up @@ -1340,6 +1340,16 @@ public function isInheritedField($fieldName)
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.
*
Expand Down
11 changes: 11 additions & 0 deletions lib/Doctrine/ORM/Mapping/MappingException.php
Expand Up @@ -211,6 +211,17 @@ public static function invalidClassInDiscriminatorMap($className, $owningClass)
);
}

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)
{
return new self("Entity class '$className' is using inheritance but no discriminator map was defined.");
Expand Down
8 changes: 4 additions & 4 deletions tests/Doctrine/Tests/ORM/Mapping/ClassMetadataFactoryTest.php
Expand Up @@ -127,7 +127,7 @@ public function testIsTransientEntityNamespace()
$this->assertTrue($em->getMetadataFactory()->isTransient('CMS:CmsUser'));
$this->assertFalse($em->getMetadataFactory()->isTransient('CMS:CmsArticle'));
}

public function testAddDefaultDiscriminatorMap()
{
$cmf = new ClassMetadataFactory();
Expand All @@ -150,9 +150,9 @@ public function testAddDefaultDiscriminatorMap()
$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('rootclass', $rootClassKey);
$this->assertEquals('childclass', $childClassKey);
$this->assertEquals('anotherchildclass', $anotherChildClassKey);

$this->assertEquals($childDiscriminatorMap, $rootDiscriminatorMap);
$this->assertEquals($anotherChildDiscriminatorMap, $rootDiscriminatorMap);
Expand Down

0 comments on commit 99e303e

Please sign in to comment.