ormdoctrine-ormdoctrinesuperclasssupertype

How to define a Doctrine mappedSuperclass using XML or YAML instead of annotation mapping


The following script comes from https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/inheritance-mapping.html#mapped-superclasses, and was only changed to include a second sub-class. It is my understanding that MappedSuperclassBase cannot exist by itself but must be extended by one and only one sub-class (i.e. either EntitySubClassOne or EntitySubClassTwo), and is the same concept as supertype/subtype for SQL. Agree?

How is a super/sub type defined using either YAML or XML instead of annotation mapping?

<?php
/** @MappedSuperclass */
class MappedSuperclassBase
{
    /** @Column(type="integer") */
    protected $mapped1;
    /** @Column(type="string") */
    protected $mapped2;
    /**
     * @OneToOne(targetEntity="MappedSuperclassRelated1")
     * @JoinColumn(name="related1_id", referencedColumnName="id")
     */
    protected $mappedRelated1;

    // ... more fields and methods
}

/** @Entity */
class EntitySubClassOne extends MappedSuperclassBase
{
    /** @Id @Column(type="integer") */
    private $id;
    /** @Column(type="string") */
    private $name;

    // ... more fields and methods
}

/** @Entity */
class EntitySubClassTwo extends MappedSuperclassBase
{
    /** @Id @Column(type="integer") */
    private $id;
    /** @Column(type="string") */
    private $name;

    // ... more fields and methods
}

Solution

  • Based on our comments, I think I see your confusion. Because the docs handle both "MappedSuperclass" and "Discriminator" on the same page, I think you've mixed up their uses in your head. Hopefully this can help you:

    A good use-case for a MappedSuperclass would be an AbstractEntity. Every Entity needs an ID, a unique identifier. It also gives you something common to check against in Listeners and such. So, go ahead and create:

    /**
     * @ORM\MappedSuperclass
     */
    abstract class AbstractEntity
    {
        /**
         * @var int
         * @ORM\Id
         * @ORM\Column(name="id", type="integer", options={"unsigned":true})
         * @ORM\GeneratedValue(strategy="IDENTITY")
         */
        protected $id;
      
        // getter / setter
    }
    

    See how this is both declared abstract and MappedSuperclass?

    This is because neither (abstract class and MappedSuperclass) cannot be instantiated on their own. You cannot do $entity = new AbstractEntity() because it's an abstract PHP class. Neither will Doctrine create a separate table for AbstractEntity.

    Next, create a Person:

    /**
     * @ORM\Entity
     * @ORM\Table(name="persons")
     *
     * @InheritanceType("JOINED")
     * @DiscriminatorColumn(name="discr", type="string")
     */
    class Person extends AbstractEntity
    {
        /**
         * @var string
         * @ORM\Column(name="name", type="string", length=255, nullable=false)
         */
        protected $name;
    
        // getter / setter
    }
    

    The above, Person, Entity is setup for Class Table Inheritance through the JOINED inheritance type. Meaning that, on the database level, the table persons will be separate from any columns added by other entities, extending Person.

    Notice how I did not declare DiscriminatorMap. Below from the docs, highlighted in bold by me:

    Things to note:

    • The @InheritanceType, @DiscriminatorColumn and @DiscriminatorMap must be specified on the topmost class that is part of the mapped entity hierarchy.
    • The @DiscriminatorMap specifies which values of the discriminator column identify a row as being of which type. In the case above a value of "person" identifies a row as being of type Person and "employee" identifies a row as being of type Employee.
    • The names of the classes in the discriminator map do not need to be fully qualified if the classes are contained in the same namespace as the entity class on which the discriminator map is applied.
    • If no discriminator map is provided, then the map is generated automatically. The automatically generated discriminator map contains the lowercase short name of each class as key.

    Now, let's create a Worker:

    /**
     * @ORM\Entity
     * @ORM\Table(name="workers")
     */
    class Worker extends Person
    {
        /**
         * @var int
         * @ORM\Column(name="worker_id", type="integer", length=11, nullable=false)
         */
        protected $workerId;
    
        // getter / setter
    }
    

    So, now we've got:

    Things to note:

    $workerId = $person->getWorkerId() // generates "method does not exist" fatal

    $workerId = $worker->getWorkerId() // generates integer value


    Hope that managed to clear stuff up for you. If not, feel free to ask.