Dag devjes,
Doctrine ORM heeft heel nice een systeem voor Class Table Inheritance, waarmee je automatisch je superclasses en subclasses ook aan aparte databasetabellen kunt koppelen. Als je bijvoorbeeld een entity Superclass
hebt, en een entity Subclass
die Superclass
extendt, dan worden bij een instance van de subclass de properties van de superclass in de superclass-tabel opgeslagen, en die van de subclass in de tabel van de subclass.
Aan de hand van een veld (@DiscriminatorColumn) in de superclass weet Doctrine wat voor soort (subclass-)object er bij de rij in de superclass-tabel hoort. Als je geen expliciete mapping (@DiscriminatorMap) opgeeft (wat in de configuratie van de Superclass moet), gebruikt hij automatisch de lowercase class name van de subclass als discriminator value. Deze manier van mapping is erg handig als je nog niet weet wat voor subclasses er zijn, bijv. omdat andere bundles/plugins nog subclasses kunnen definiëren.
De oplettende lezer ziet hier ook meteen het nadeel van. Als je in verschillende PHP-namespaces twee zulke subclasses hebt die dezelfde naam hebben, krijgen die ook automatisch dezelfde discriminator value. Dat is (natuurlijk) onbegrijpelijk voor Doctrine, die dan een error geeft:
The entries ... in discriminator map of class 'EntityClass' 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: ...
Om dit op te lossen, kun je dus in een @DiscriminatorMap
alle subclasses expliciet definiëren, óf je gebruikt de volgende Event Subscriber. Wat deze doet, is als je in je @DiscriminatorMap
een key #UseClassNames
(je kunt aanpassen wat het precies is) zet, dan gebruikt hij de FQCN (Fully Qualified Class Name, dus incl. namespace) van de subclass als discriminator value. Het probleem wat je met een gelijknamige class in verschillende namespaces had, krijg je nu niet meer!
In je Entity-definitie kun je dan het volgende doen om aan te geven dat je dit wil gebruiken:
/**
* @ORM\Entity
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn("type")
* @ORM\DiscriminatorMap({"#UseClassNames": MyEntity::class})
* @ORM\Table(name="my_entity")
*/
abstract class MyEntity {
....
}
Ik hoop dat jullie hier iets aan hebben. Hier is de event subscriber. Vergeet hem niet aan je Doctrine-configuratie (of service definition) toe te voegen.
Veel plezier ermee!
@Xesau
<?php
use Doctrine\ORM\{ EntityManagerInterface, Events };
use Doctrine\ORM\Event\LoadClassMetadataEventArgs;
use Doctrine\ORM\Mapping\Table;
use Doctrine\Common\EventSubscriber;
use ReflectionClass;
/**
* This listener changes discriminator maps to use the FQCNs instead of
* a lowercase class name proper.
*/
class DoctrineInheritanceMappingSubscriber implements EventSubscriber
{
private $allMappedClassNames = [];
public function __construct(EntityManagerInterface $em)
{
$this->allMappedClassNames = $em->getConfiguration()->getMetadataDriverImpl()->getAllClassNames();
}
public function getSubscribedEvents()
{
return [Events::loadClassMetadata];
}
public function loadClassMetadata(LoadClassMetadataEventArgs $event)
{
$metadata = $event->getClassMetadata();
if (is_array($metadata->discriminatorMap) && isset($metadata->discriminatorMap['#UseClassNames']))
{
$metadata->discriminatorValue = $metadata->name;
$metadata->discriminatorMap = [
$metadata->name => $metadata->name
];
foreach ($this->allMappedClassNames as $className)
{
if (in_array($metadata->name, class_parents($className)))
{
$metadata->discriminatorMap[$className] = $className;
$metadata->subClasses[] = $className;
}
}
}
}
}