# Doctrine ORM and abstract entities

**URL:** <https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197>\
**Category:** 3rd-Party Tools & Libraries\
**Tags:** doctrine\
**Created:** [March 23, 2021, 10:17am UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197 "2021-03-23T10:17:32Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ezkimo](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ezkimo/32/781_2.png) [@ezkimo](https://discourse.laminas.dev/u/ezkimo)\
**Post date:** [March 23, 2021, 10:17am UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/1 "2021-03-23T10:17:32Z")

</div>

Hey,

I 've got an issue with aggregating entities in Doctrine ORM 2.x. I 've seen the `ResolveTargetEntityListener` solution provided by Doctrine. This seems to work for exactly one abstraction as seen in the [Doctrine documentation](https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/cookbook/resolve-target-entity-listener.html). But what if I have more than one inheritation from an abstract entity or more than one implementation of an interface?

A short example …

```auto
declare(strict_types=1);
namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\MappedSuperclass
 */
abstract class Partner
{
    /**
     * @ORM\Column(name="name", type="string", length=255, nullable=false)
     */
    protected ?string $name;
}

```

As you can see, this is the abstract entity from which other entities will derive. The inherited entities are somewhat like the following two. For the example, I will mention only two. There are far more entities.

```auto
declare(strict_types=1);
namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Represents a natural person
 *
 * @ORM\Entity
 * @ORM\Table(name="natural_person")
 */
class NatualPerson extends Partner
{
    /**
     * @var int
     * @ORM\Id @ORM\Column(type="integer", name="id")
     * @ORM\GeneratedValue
     */
    protected int $id;
}

```

… and the other one …

```auto
declare(strict_types=1);
namespace Application\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Represents a legal person (companies)
 *
 * @ORM\Entity
 * @ORM\Table(name="legal_person")
 */
class LegalPerson extends Partner
{
    /**
     * @var int
     * @ORM\Id @ORM\Column(type="integer", name="id")
     * @ORM\GeneratedValue
     */
    protected int $id;
}

```

A group can contain zero or more partners. A collection of natural and legal persons.

```auto
declare(strict_types=1);
namespace Application\Entity;

use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="group")
 */
class Group
{
    /**
     *
     * @var int
     * @ORM\Id @ORM\Column(type="integer", name="id")
     * @ORM\GeneratedValue
     */
    protected $id;
    
    /**
     * @ORM\ManyToOne(targetEntity="Application\Entity\Partner")
     * @var Partner
     */
    protected Collection $members;

    public function __construct()
    {
        $this->members = new ArrayCollection();
    }
}

```

If I 'm using the example from the above mentioned doctrine cookbook example, this won 't work.

```auto
$evm = new \Doctrine\Common\EventManager;
$rtel = new \Doctrine\ORM\Tools\ResolveTargetEntityListener;

// Adds a target-entity class
$rtel->addResolveTargetEntity(Partner::class, LegalPerson::class, []);
$rtel->addResolveTargetEntity(Partner::class, NaturalPerson::class, []);

// Add the ResolveTargetEntityListener
$evm->addEventListener(Doctrine\ORM\Events::loadClassMetadata, $rtel);

$em = \Doctrine\ORM\EntityManager::create($connectionOptions, $config, $evm);

```

Question: Is there another smart way to reach something like aggregation based on an abstract entity or an interface, that aggregates to more than one target entity?

---

<div class="post-metadata">

**Author:** ![timdev](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/timdev/32/224_2.png) [@timdev](https://discourse.laminas.dev/u/timdev)\
**Post date:** [March 27, 2021, 7:00pm UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/2 "2021-03-27T19:00:42Z")

</div>

I don’t think you need ResolveTargetEntityListener at all, based on your example.

The purpose of `ResolveTargetEntityListener` is to resolve an interface to a class at runtime, which is useful if you’re trying to decouple modules. In the cookbook example, the idea is to avoid having the CustomerModule and InvoiceModule not depend on each other. In the example, an `Invoice` can have a subject which must implement an interface. RTEL allows the Application module to compose the two (independent) modules.

You don’t appear to be doing that in your example. It looks like you simply want to target an interface or abstract class in an association. You can just do that directly, without involving RTEL.

It looks like you don’t need anything that isnt’ described in the [Inheritance Mapping chapter](https://www.doctrine-project.org/projects/doctrine-orm/en/2.8/reference/inheritance-mapping.html#inheritance-mapping) of the ORM documenation.

---

<div class="post-metadata">

**Author:** ![ezkimo](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ezkimo/32/781_2.png) [@ezkimo](https://discourse.laminas.dev/u/ezkimo)\
**Post date:** [March 27, 2021, 7:31pm UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/3 "2021-03-27T19:31:01Z")

</div>

Thanks for your answer tim. But my problem is not inheritance mapping. My problem is a relation to an entity property, which is an interface or an abstract class. Perhaps my example was not clear enough.

What happens, when my `Group` class collects both `NaturalPerson` and `LegalPerson` entities in its `members` collection? Both are inheritations of `Partner`. How does doctrine get the join tables and columns?

---

<div class="post-metadata">

**Author:** ![timdev](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/timdev/32/224_2.png) [@timdev](https://discourse.laminas.dev/u/timdev)\
**Post date:** [March 27, 2021, 7:40pm UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/4 "2021-03-27T19:40:33Z")

</div>

It’s possible I’m missing something, but from memory, you can just target the mapped superclass and it will just work. What breaks if you just remove all the RTE related code?

---

<div class="post-metadata">

**Author:** ![timdev](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/timdev/32/224_2.png) [@timdev](https://discourse.laminas.dev/u/timdev)\
**Post date:** [March 27, 2021, 8:12pm UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/5 "2021-03-27T20:12:23Z")

</div>

After reviewing the docs (it’s been some time since I did this), I realize you can’t use mapped superclass like that.

What you probably want is class-table inheritance with an abstract Partner entity. You’ll end up with three tables in your schema.

Like this: [GitHub - timdev/doctrine-inheritance-demo: https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/3](https://github.com/timdev/doctrine-inheritance-demo)

---

<div class="post-metadata">

**Author:** ![ALTAMASH80](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/altamash80/32/895_2.png) [@ALTAMASH80](https://discourse.laminas.dev/u/ALTAMASH80)\
**Post date:** [March 29, 2021, 7:22am UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/6 "2021-03-29T07:22:25Z")

</div>

Hi,

My dumb question would be, why are you making a legal person, a neutral person as an entity? Those are characteristic of a person. So, shouldn’t it be a characteristic entity rather than each entity for each characteristic? Sorry, just wanted to understand your approach. You must have some reason behind it. Maybe you’re dealing with some legacy code. Therefore, I’m giving a view of what I understood. Thanks!

---

<div class="post-metadata">

**Author:** ![ezkimo](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ezkimo/32/781_2.png) [@ezkimo](https://discourse.laminas.dev/u/ezkimo)\
**Post date:** [April 1, 2021, 6:26am UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/7 "2021-04-01T06:26:27Z")

</div>

Sorry for the late reply and a big thanks for the demo. That is exactly what I 'm looking for.  
Very good approach. I 'll use this.

Again, thank you and happy easter everyone.

---

<div class="post-metadata">

**Author:** ![ezkimo](https://yyz2.discourse-cdn.com/flex032/user_avatar/discourse.laminas.dev/ezkimo/32/781_2.png) [@ezkimo](https://discourse.laminas.dev/u/ezkimo)\
**Post date:** [April 1, 2021, 7:10am UTC](https://discourse.laminas.dev/t/doctrine-orm-and-abstract-entities/2197/8 "2021-04-01T07:10:04Z")

</div>

Because legal persons and natural persons have different properties. The abstract partner class contains the properties that both natural and legal persons can have. The derived classes for legal and natural persons contain the specific properties that apply only to legal or natural persons.

Short example: A natural person has a `$prename` property. Legal persons do not have a `$prename` property. Legal persons have a `$sales` property, while natural persons have not. Both of them have a `$name` property.

Conclusion: There are to many properties that differ between legal and natural persons. At the same time they have properties in common. So there has to be an abstraction from which both of them inherit.
