diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index d70f02ffa..089780775 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -70,6 +70,18 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory */ private $embeddablesActiveNesting = array(); + /** + * {@inheritDoc} + */ + protected function loadMetadata($name) + { + $loaded = parent::loadMetadata($name); + + array_map([$this, 'resolveDiscriminatorValue'], array_map([$this, 'getMetadataFor'], $loaded)); + + return $loaded; + } + /** * @param EntityManagerInterface $em */ @@ -270,9 +282,6 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory if ( ! $class->discriminatorColumn) { throw MappingException::missingDiscriminatorColumn($class->name); } - } else if ($parent && !$class->reflClass->isAbstract() && !in_array($class->name, array_values($class->discriminatorMap))) { - // enforce discriminator map for all entities of an inheritance hierarchy, otherwise problems will occur. - throw MappingException::mappedClassNotPartOfDiscriminatorMap($class->name, $class->rootEntityName); } } else if ($class->isMappedSuperclass && $class->name == $class->rootEntityName && (count($class->discriminatorMap) || $class->discriminatorColumn)) { // second condition is necessary for mapped superclasses in the middle of an inheritance hierarchy @@ -288,6 +297,47 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory return new ClassMetadata($className, $this->em->getConfiguration()->getNamingStrategy()); } + /** + * Populates the discriminator value of the given metadata (if not set) by iterating over discriminator + * map classes and looking for a fitting one. + * + * @param ClassMetadata $metadata + * + * @return void + * + * @throws MappingException + */ + private function resolveDiscriminatorValue(ClassMetadata $metadata) + { + if ($metadata->discriminatorValue + || ! $metadata->discriminatorMap + || $metadata->isMappedSuperclass + || $metadata->reflClass->isAbstract() + ) { + return; + } + + // minor optimization: avoid loading related metadata when not needed + foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { + if ($discriminatorClass === $metadata->name) { + $metadata->discriminatorValue = $discriminatorValue; + + return; + } + } + + // iterate over discriminator mappings and resolve actual referenced classes according to existing metadata + foreach ($metadata->discriminatorMap as $discriminatorValue => $discriminatorClass) { + if ($metadata->name === $this->getMetadataFor($discriminatorClass)->getName()) { + $metadata->discriminatorValue = $discriminatorValue; + + return; + } + } + + throw MappingException::mappedClassNotPartOfDiscriminatorMap($metadata->name, $metadata->rootEntityName); + } + /** * Adds a default discriminator map if no one is given * diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php index 67567456c..c3c8d8c83 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php @@ -2745,15 +2745,18 @@ class ClassMetadataInfo implements ClassMetadata $className = ltrim($className, '\\'); $this->discriminatorMap[$name] = $className; - if ($this->name == $className) { + if ($this->name === $className) { $this->discriminatorValue = $name; - } else { - if ( ! class_exists($className)) { - throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); - } - if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) { - $this->subClasses[] = $className; - } + + return; + } + + if ( ! (class_exists($className) || interface_exists($className))) { + throw MappingException::invalidClassInDiscriminatorMap($className, $this->name); + } + + if (is_subclass_of($className, $this->name) && ! in_array($className, $this->subClasses)) { + $this->subClasses[] = $className; } } diff --git a/tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php index f145ba259..3055b32e1 100644 --- a/tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php +++ b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/JoinedRootClass.php @@ -5,7 +5,7 @@ namespace Doctrine\Tests\Models\CompositeKeyInheritance; * @Entity * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"child" = "JoinedChildClass",}) + * @DiscriminatorMap({"child" = "JoinedChildClass", "root" = "JoinedRootClass"}) */ class JoinedRootClass { diff --git a/tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleRootClass.php b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleRootClass.php index 1bbd09702..19b4b4b01 100644 --- a/tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleRootClass.php +++ b/tests/Doctrine/Tests/Models/CompositeKeyInheritance/SingleRootClass.php @@ -5,7 +5,7 @@ namespace Doctrine\Tests\Models\CompositeKeyInheritance; * @Entity * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"child" = "SingleChildClass",}) + * @DiscriminatorMap({"child" = "SingleChildClass", "root" = "SingleRootClass"}) */ class SingleRootClass { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1113Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1113Test.php index 5bb007fd0..335c89dce 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1113Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1113Test.php @@ -50,7 +50,7 @@ class DDC1113Test extends \Doctrine\Tests\OrmFunctionalTestCase /** * @Entity * @InheritanceType("SINGLE_TABLE") - * @DiscriminatorMap({"car" = "DDC1113Car", "bus" = "DDC1113Bus"}) + * @DiscriminatorMap({"vehicle" = "DDC1113Vehicle", "car" = "DDC1113Car", "bus" = "DDC1113Bus"}) */ class DDC1113Vehicle { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1454Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1454Test.php index 162072965..f6b26253a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1454Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1454Test.php @@ -43,7 +43,7 @@ class DDC1454Picture extends DDC1454File * @Entity * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"picture" = "DDC1454Picture"}) + * @DiscriminatorMap({"file" = "DDC1454File", "picture" = "DDC1454Picture"}) */ class DDC1454File { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php index 6ba0cd42f..2b7e9b377 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1509Test.php @@ -115,7 +115,7 @@ class DDC1509Picture * @Entity * @InheritanceType("SINGLE_TABLE") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"file" = "DDC1509File"}) + * @DiscriminatorMap({"abstractFile" = "DDC1509AbstractFile", "file" = "DDC1509File"}) */ class DDC1509AbstractFile { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1787Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1787Test.php index 39fe48367..862d9196a 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1787Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC1787Test.php @@ -33,7 +33,7 @@ class DDC1787Test extends \Doctrine\Tests\OrmFunctionalTestCase * @Entity * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"bar" = "DDC1787Bar"}) + * @DiscriminatorMap({"bar" = "DDC1787Bar", "foo" = "DDC1787Foo"}) */ class DDC1787Foo { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php index 549cf23cb..92510cb02 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2012Test.php @@ -71,7 +71,8 @@ class DDC2012Test extends \Doctrine\Tests\OrmFunctionalTestCase * @InheritanceType("JOINED") * @DiscriminatorColumn(name="type_id", type="smallint") * @DiscriminatorMap({ - * 1 = "DDC2012ItemPerson" + * 1 = "DDC2012ItemPerson", + * 2 = "DDC2012Item" * }) */ class DDC2012Item diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2346Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2346Test.php index 8bc803f29..8832b0f50 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2346Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2346Test.php @@ -88,7 +88,7 @@ class DDC2346Foo * @Entity * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") - * @DiscriminatorMap({"baz" = "DDC2346Baz"}) + * @DiscriminatorMap({"bar" = "DDC2346Bar", "baz" = "DDC2346Baz"}) */ class DDC2346Bar { diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3223Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3223Test.php index 5677075dd..541221aa0 100644 --- a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3223Test.php +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3223Test.php @@ -53,7 +53,8 @@ class Journalist extends Participant * @InheritanceType("JOINED") * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({ - * "journalist" = "Journalist", + * "journalist" = "Journalist", + * "participant" = "Participant", * }) */ class Participant @@ -71,6 +72,7 @@ class Participant * @DiscriminatorColumn(name="discr", type="string") * @DiscriminatorMap({ * "profile" = "ProfileStatus", + * "status" = "Status", * }) */ class Status diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3300Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3300Test.php new file mode 100644 index 000000000..b7905ca3d --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC3300Test.php @@ -0,0 +1,85 @@ +addResolveTargetEntity( + DDC3300BossInterface::INTERFACENAME, + DDC3300Boss::CLASSNAME, + array() + ); + + $resolveTargetEntity->addResolveTargetEntity( + DDC3300EmployeeInterface::INTERFACENAME, + DDC3300Employee::CLASSNAME, + array() + ); + + $this->_em->getEventManager()->addEventSubscriber($resolveTargetEntity); + + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(DDC3300Person::CLASSNAME), + )); + + $boss = new DDC3300Boss(); + $employee = new DDC3300Employee(); + + $this->_em->persist($boss); + $this->_em->persist($employee); + + $this->_em->flush(); + $this->_em->clear(); + + $this->assertEquals($boss, $this->_em->find(DDC3300BossInterface::INTERFACENAME, $boss->id)); + $this->assertEquals($employee, $this->_em->find(DDC3300EmployeeInterface::INTERFACENAME, $employee->id)); + } +} + +/** + * @Entity + * @InheritanceType("SINGLE_TABLE") + * @DdiscriminatorColumn(name="discr", type="string") + * @DiscriminatorMap({ + * "boss" = "Doctrine\Tests\ORM\Functional\Ticket\DDC3300BossInterface", + * "employee" = "Doctrine\Tests\ORM\Functional\Ticket\DDC3300EmployeeInterface" + * }) + */ +abstract class DDC3300Person +{ + const CLASSNAME = __CLASS__; + + /** @Id @Column(type="integer") @GeneratedValue(strategy="AUTO") */ + public $id; +} + +interface DDC3300BossInterface +{ + const INTERFACENAME = __CLASS__; +} + +/** @Entity */ +class DDC3300Boss extends DDC3300Person implements DDC3300BossInterface +{ + const CLASSNAME = __CLASS__; +} + +interface DDC3300EmployeeInterface +{ + const INTERFACENAME = __CLASS__; +} + +/** @Entity */ +class DDC3300Employee extends DDC3300Person implements DDC3300EmployeeInterface +{ + const CLASSNAME = __CLASS__; +}