Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map subclass as its extended parent

I have created the following abstract class, which use single table inheritance and maps subclasses on the DiscriminatorColumn model.

/**
 * @Entity
 * @Table(name="entity")
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(name="model", type="string")
 * @DiscriminatorMap({
 *      "green" = "model\GreenEntity",
 *      "blue"  = "model\BlueEntity"
 * })
 */
abstract class AbstractEntity
{
    /** @Id @Column(type="string") */
    protected $entity_id;
}

Let's say I extend the abstract class AbstractEntity by some classes:

class GreenEntity extends AbstractEntity {}
class BlueEntity extends AbstractEntity {}

And extend these by some more subclasses

class GreenEntityChildOne extends GreenEntity {}
class GreenEntityChildTwo extends GreenEntity {}
class BlueEntityChildOne extends BlueEntity {}
class BlueEntityChildTwo extends BlueEntity {}

Now, for example, when I instantiate GreenEntityChildOne and persist it to the database, it will throw an exception that I don't have a mapping for it.

What I'm trying to do is get GreenEntityChildOne to be mapped as GreenEntity (or rather, every class which extends a class below AbstractEntity to be mapped as the class which extends the upper abstract class).

Is this at all possible?

like image 605
Bas Peeters Avatar asked Sep 16 '15 08:09

Bas Peeters


1 Answers

It's not possible with pure annotations

Yes, the mapping you are trying to achieve is possible. However, not with pure annotations. The important thing is that Doctrine needs to know all sub classes at runtime. If you do not want to state them explicitly in the annotations of the mapped superclass, you will need to dynamically provide them.

Doctrine event system to the rescue

There is a great blog post on dynamic mapping with Doctrine, which explains how you can use Doctrine event listeners to programmatically change the loaded ClassMetadata. To dynamically add subclasses to the discriminator map you can implement a Doctrine event listener like the following:

class DynamicDiscriminatorMapSubscriber implements EventSubscriber
{
    public function getSubscribedEvents()
    {
        return array(Events::loadClassMetadata);
    }

    public function loadClassMetadata(LoadClassMetadataEventArgs $eventArgs)
    {
        $metadata = $eventArgs->getClassMetadata();
        $metadata->addDiscriminatorMapClass("GreenEntityChildOne", GreenEntityChildOne::class);
    }
}

Register your subscriber

Now you only need to register the event subscriber with Doctrine. Ideally, you inject the classes you want to add based on your configuration to the event subscriber.

// create dynamic subscriber based on your config which contains the classes to be mapped
$subscriber = new DynamicDiscriminatorMapSubscriber($config);
$entityManager->getEventManager()->addEventSubscriber($subscriber);

Further reading

Also, have a look at the PHP mapping section in the Doctrine manual and the more informative API docs for the ClassMetadataBuilder.

like image 120
Fabian Keller Avatar answered Oct 10 '22 21:10

Fabian Keller