12

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?

sroes
  • 14,663
  • 1
  • 53
  • 72
  • 1
    Possible duplicate of [Can I access discriminator field from php in doctrine2?](http://stackoverflow.com/questions/4450381/can-i-access-discriminator-field-from-php-in-doctrine2) – Maks3w Jan 23 '16 at 15:42

4 Answers4

23

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';
Cerad
  • 48,157
  • 8
  • 90
  • 92
  • 2
    This doesn't help when you need you'd need to access your discriminator in a WHERE clause. `INSTANCE OF` kinda does, but this data masking looks really weird (and ugly) to me. Only brings complexity and limitations, imo. – Balmipour Sep 28 '20 at 15:56
  • You might be missing the point. The discriminator is only accessible by Doctrine. You can't access it in a DQL statement. Instance of is the DQL method. And yes there are plenty of limits. But SQL itself does not allow establishing relations with between a single column and multiple tables. Be kind of nice if it did. – Cerad Sep 28 '20 at 18:40
  • I noticed that, sadly. What I don't undersand is why Doctrine doesn't allow one to get the value, which is highly useful (be it for a "where", or for any computation needing it.) I just had to use this feature in a recent dev, but having an idea of the performance impacts it has, doing all joins before the restrictions (*WHERE clause*), and putting **strings** as discriminator keys in their examples really puzzles me. _My comment's point was to tell about `INSTANCE OF`., since Doctrine makes this pretty complex_. No answer speaks of it here. – Balmipour Sep 28 '20 at 23:29
  • Did you happen to notice the date? I am not positive that Doctrine had the instance of operator back in 2014. And the question of course was about accessing the discriminator column. Not queries. But no matter how you implement it, trying to map inheritance to a sql database is awkward. – Cerad Sep 28 '20 at 23:50
  • I didn't pay attention to the date, but just wanted add a hint, since this question probably has a quite good ranking in search results. Anyway, fully agree with you, and didn't meant to bother you with comments. Just wanted to add some info I thought useful, and which I didn't found here. – Balmipour Sep 29 '20 at 07:42
3

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.

Andris
  • 5,853
  • 3
  • 28
  • 34
3

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 {}
Gregory Boutte
  • 584
  • 6
  • 26
automatix
  • 14,018
  • 26
  • 105
  • 230
1

You can use the following solution:

`$`$metadata = \Doctrine\ORM\Mapping\ClassMetadata((string)$entityName);
print_r($metadata->discriminatorValue);`
Steve
  • 9,270
  • 5
  • 47
  • 61
Ketsuo
  • 11
  • 1