Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Map a discriminator column to a field with Doctrine 2

Tags:

doctrine-orm

In my project I have several class table inheritances like this:

namespace MyProject\Model;

/**
 * @Entity
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="discr", type="string")
 * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
 */
class Person
{
    // ...
}

/** @Entity */
class Employee extends Person
{
    // ...
}

I have a method which converts entities to arrays based on the fields which have public getters. The problem here is that I lose the inheritance information in my array because the discriminator value isn't stored in a field.

So what I tried was the following, hoping doctrine would automatically set $disc:

class Person
{
    // can I automatically populate this field with 'person' or 'employee'?
    protected $discr;

    public function getDiscr() { return $this->discr; }
    public function setDiscr($disc) { $this->discr; }

    // ...
}

Is there a way to make this work in doctrine? Or would I need to read the class metadata in my entity-to-array method?

like image 454
sroes Avatar asked Jan 22 '14 13:01

sroes


2 Answers

Sadly, there is no documented way to map the discr column to an entity. That's because the discr column is really part of the database and not the entity.

However, it's quite common to just put the discr value directly in your class definition. It's not going to change and you will always get the same class for the same value anyways.

class Person
{
    protected $discr = 'person';

class Employee extends Person
{
    protected $discr = 'employee';
like image 154
Cerad Avatar answered Sep 19 '22 18:09

Cerad


Here's a small example of what I have in one of my ZF2 projects (using Doctrine MongoDB ODM):

// an instance of your entity
$entity = ...;

/** @var \Doctrine\ODM\MongoDB\DocumentManager $documentManager */
$documentManager = $serviceManager->get('DocumentManager');

/** @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadataFactory $factory */
$factory = $documentManager->getMetadataFactory()

/** @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $metadata */
$metadata = $factory->getMetadataFor(get_class($object));

if ($metadata->hasDiscriminator()) {
     // assuming $data is result of the previous extraction
     $data[$metadata->discriminatorField] = $metadata->discriminatorValue;
}

What I have done is I've implemented a custom interface DiscriminatorAwareInterface and I only apply the checks to classes that implement it (in your case it would be the class that all "discriminated" classes extend.

As a result I end up with code that looks like this:

// add value of the discrinimator field to entities that support it
if ($object instanceof DiscriminatorAwareInterface) {
    /** @var \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $metadata */
    $metadata = $factory->getMetadataFor(get_class($object));

    if ($metadata->hasDiscriminator()) {
        $data[$metadata->discriminatorField] = $metadata->discriminatorValue;
    }
}

I'm pretty sure it will be the same if you use the standard ORM, except instead of a document manager you will have entity manager.

like image 37
Andris Avatar answered Sep 21 '22 18:09

Andris