sroes
sroes

Reputation: 15053

Map a discriminator column to a field with Doctrine 2

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?

Upvotes: 12

Views: 24302

Answers (4)

automatix
automatix

Reputation: 14532

Just got this problem and solved it without defining the discriminator as a real member:

abstract class MyEntity {
    const TYPE_FOO = 'foo';
    const TYPE_BAR = 'bar';
    const TYPE_BUZ = 'buz';
    ...
    /**
     * @return string
     */
    public function getMyDiscriminator()
    {
        $myDiscriminator = null;
        switch (get_class($this)) {
            case MyEntityFoo::class:
                $myDiscriminator = self::TYPE_FOO;
                break;
            case MyEntityBar::class:
                $myDiscriminator = self::TYPE_BAR;
                break;
            case MyEntityBuz::class:
                $myDiscriminator = self::TYPE_BUZ;
                break;
        }
        return $myDiscriminator;
    }
    ...
}

class MyEntityFoo extends MyEntity {}

class MyEntityBar extends MyEntity {}

class MyEntityBuz extends MyEntity {}

Upvotes: 3

Andris
Andris

Reputation: 6113

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.

Upvotes: 3

Ketsuo
Ketsuo

Reputation: 11

You can use the following solution:

`$`$metadata = \Doctrine\ORM\Mapping\ClassMetadata((string)$entityName);
print_r($metadata->discriminatorValue);`

Upvotes: 1

Cerad
Cerad

Reputation: 48865

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';

Upvotes: 24

Related Questions