Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doctrine Inheritance replacement

Currenty we're developing a very flexible and modular application with Zend Framework 2 and Doctrine 2. In this application there are multiple Doctrine entities, for example let's say the entity Product in the module Products. This module Products is the base/default module for product management.

We want to be able to create a custom Products module for a customer (XProducts). Therefore I created a new entity, XProduct (with some extra fields) which extends Product.

So if the custom module is enabled I want to use XProduct and else Product, but never together (in the same project).

If I annotate both entities with @Entity it works partially; for example findAll works perfect, but find doesn't work: the created SELECT statement contains the correct columns, but the WHERE clause is wrong. For example:

SELECT t1.id AS id2, t1.name AS name3 FROM products t1 WHERE t0.id = ?

I guess t1 stands for ProductX and t0 for Product but I can't figure out why the columns are correct (t1) but the where clause isn't (t0).

I'm aware that Doctrine provides Single Table Inheritance to achieve inheritance, but therefore it's necessary to have a DiscriminatorColumn and to define the DiscriminatorMap at the base/default entity. This won't fit for us, because we need to change our base/default module if we add a new custom module for a customer (and that's not what we want...).

Does anyone have a clue on fixing this problem? Thanks!

like image 647
Tom Drissen Avatar asked Jul 11 '13 08:07

Tom Drissen


2 Answers

I finally fixed this problem. For all default/base class I created an extra abstract MappedSuperclass (as Jurian Sluiman mentioned). For example, for a specific Product entity for a customer I need the following:

  • AbstractProduct (contains all default/base functionalities)
  • Product (default/base class which is empty and extends AbstractProduct)
  • XProduct (which contains extra functionalities for our customer and extends AbstractProduct)

To fix the problems with associations on a MappedSuperclass I refer to the abstract class, for example: @ORM\OneToOne(targetEntity="ProductManagement\Entity\AbstractProduct")

I then use Doctrine's EntityResolver (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/resolve-target-entity-listener.html) to map an abstract class (or interface) association to a real entity (depending on the configuration):

'entity_resolver' => array(
    'orm_default' => array(
        'resolvers' => array(
            // Note: Use only one
            'ProductManagement\Entity\AbstractProduct' => 'ProductManagement\Entity\Product', // Default
            'ProductManagement\Entity\AbstractProduct' => 'XProductManagement\Entity\XProduct', // For customer X
        )
    )
)

This way I'm able to override my entities with specific entities for my customers, without changing the default/base module and entities (which is exactly what I was looking for).

like image 139
Tom Drissen Avatar answered Oct 20 '22 01:10

Tom Drissen


We use this pattern as well as it's with Doctrine the easiest to work with (though it can be made OOP wise much better with a lot of ugly code). Take an example of our Portfolio module where a Portfolio instance can take up multiple Item instances.

We work with a Portfolio entity which extends from the mapped superclass AbstractPortfolio. If we have a client requiring a special field, we create a ClientPortfolio extending the mapped superclass, so it overloads all properties correctly.

The class name is specified in the config and that string is used in for example the factory for the repository. You never load the Portfolio repository, but always load the ClientPortfolio even when you request the repository class from the service manager under the name of the default portfolio.

This method can work fine with repository functions like here (though this class is the repository for an Item and not the Portfolio). I would not use single table inheritance as you do not use multiple entities besides each other. At least, that is our case.

like image 28
Jurian Sluiman Avatar answered Oct 20 '22 03:10

Jurian Sluiman