Can I access discriminator field from php in doctr

2019-01-08 19:07发布

问题:

I have an entity which defines inheritance like this:

* @DiscriminatorColumn(name="type", type="string")
* @DiscriminatorMap({"text" = "TextAttribute", "boolean" = "BooleanAttribute", "numeric" = "NumericAttribute", "date" = "DateAttribute"})

I am wondering is it possible to have getter for field 'type'? I know I can use instanceof (and in most cases this is what I'm doing) but there are few scenarios where $item->getType() would make my life so much easier.

回答1:

Extending what beberlei said, you could declare some constants in the Attribute class, and an abstract getType() function. Then, overload it in every derived attribute class.

Something like:

abstract class Attribute {
    const TYPE_BOOL = 0;
    const TYPE_INT  = 1;
    ...
    abstract public function getType();
}

class BooleanAttribute extends Attribute {
    public function getType() {
        return parent::TYPE_BOOL;
    }
}


回答2:

Here is how I'd do.

First, you made an AttributeInterface, to be sure that all future new Attribute types will implement the need method :

interface AttributeInterface
{
    /**
     * Return the attribute type
     */
    public function getType();
}

Then you create the Attribute abstract class implementing the AttributeInterface interface.

Use the constants in the @DiscrimatorMap call for some consistency

/**
 * Attribute
 * ...
 * @DiscriminatorColumn(name="type", type="string")
 * @DiscriminatorMap({Attribute::TYPE_TEXT = "TextAttribute", Attribute::TYPE_BOOLEAN = "BooleanAttribute", Attribute::TYPE_NUMERIC = "NumericAttribute", Attribute::TYPE_DATE = "DateAttribute"})
 */
abstract class Attribute implements AttributeInterface
{
    const TYPE_TEXT    = 'text';
    const TYPE_BOOLEAN = 'boolean';
    const TYPE_NUMERIC = 'numeric';
    const TYPE_DATE    = 'date';
}

Finally, you create all needed classes, extending Attribute class and implementing the getType() method

/**
 * TextAttribute
 *
 * @ORM\Entity
 */
class TextAttribute extends Attribute
{
    public function getType()
    {
        return $this::TYPE_TEXT;
    }
}

/**
 * BooleanAttribute
 *
 * @ORM\Entity
 */
class BooleanAttribute extends Attribute
{
    public function getType()
    {
        return $this::TYPE_BOOLEAN;
    }
}

/**
 * NumericAttribute
 *
 * @ORM\Entity
 */
class  NumericAttribute extends Attribute
{
    public function getType()
    {
        return $this::TYPE_NUMERIC;
    }
}

/**
 * DateAttribute
 *
 * @ORM\Entity
 */
class DateAttribute extends Attribute
{
    public function getType()
    {
        return $this::TYPE_DATE;
    }
}

// And so on...


回答3:

It's possible either with the EntityManager or using the DocumentManager.

$documentManager->getClassMetadata(get_class($entity))->discriminatorValue;


回答4:

My approach is to simply access it's value through the meta data doctrine generates

$cmf = $em->getMetadataFactory();
$meta = $cmf->getMetadataFor($class);
$meta->discriminatorValue

will give you the value, so as a method

public static function get_type()
{
    //...get the $em instance 
    $cmf = $em->getMetadataFactory();
    $meta = $cmf->getMetadataFor(__CLASS__);
    return $meta->discriminatorValue;
}

I cache the metadata in a static variable for each class that extends my base entity, there is a lot of other useful information in there as well ...



回答5:

No that is not possible, but you can do something like: get_class($object) == TYPE_CONST



回答6:

There's a slicker way to do it in PHP 5.3:

abstract Parent
{
    const TYPE = 'Parent';

    public static function get_type()
    {
        $c = get_called_class();
        return $c::TYPE;
    }
}

class Child_1 extends Parent
{
    const TYPE = 'Child Type #1';
    //..whatever
}

class Child_2 extends Parent
{
    const TYPE = 'Child Type #2';
    //...whatever
}


回答7:

Use something like this if you want, like me, avoid use of const :

public function getType()
{
    $type = explode('\\', get_class($this));

    return end($type);
}


回答8:

Another slicker way than to overload the method in every child, with native symfony :

public function getType() {
    return (new \ReflectionClass($this))->getShortName();
}

It might not return exactly the discriminator name depending on your discriminator map declaration but it will return the child entity name (the class name) which is a great way to name and distinguish the different subentities

Without a need to define anything in the subclasses.