Replacing Doctrine Inheritance

We are currently developing a very flexible and modular application with Zend Framework 2 and Doctrine 2. There are several Doctrine objects in this application, for example, let the Product object in the Products module say. This Products module is the basic / standard module for product management.

We want to create a custom Products module for the client ( XProducts ). Therefore, I created a new XProduct object (with some additional fields) that extends Product .

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

If I annotate both objects with @Entity, it works partially; for example, findAll works fine, but find does not work: the generated SELECT statement contains the correct columns, but the WHERE clause is incorrect. For instance:

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

I think t1 means ProductX and t0 for Product , but I cannot understand why the columns are correct ( t1 ), but the where clause is not ( t0 ).

I know that Doctrine provides one-page inheritance to achieve inheritance, but so you need to have a DiscriminatorColumn and define a DiscriminatorMap in the database / default. This is not suitable for us, because we need to change our base / default module if we add a new user module for the client (and this is not what we want ...).

Does anyone have a clue to fix this problem? Thanks!

+6
source share
2 answers

I finally fixed this problem. For all default / base classes, I created an additional abstract MappedSuperclass (as Jurian Sluiman mentioned). For example, for a specific Product object for a client, I need the following:

  • AbstractProduct (contains all standard / basic functions)
  • Product (standard / base class that is empty and extends AbstractProduct)
  • XProduct (which contains additional features for our client and extends AbstractProduct)

To fix association problems in MappedSuperclass, I refer to an abstract class, for example: @ORM\OneToOne(targetEntity="ProductManagement\Entity\AbstractProduct")

Then I use the Doctrine EntityResolver (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/resolve-target-entity-listener.html ) to map the association of the abstract class (or interface) with a real object (depending on 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 ) ) ) 

Thus, I can redefine my entities with specific objects for my clients without changing the default module / base module and objects (this is exactly what I was looking for).

+7
source

We use this template as well as with Doctrine, which is easiest to work with (although it can be made much easier with lots of ugly code). Take an example of our Portfolio , where an instance of Portfolio can accept multiple instances of Item .

We are working with a Portfolio object that extends from the displayed superclass AbstractPortfolio . If we have a client that requires a special field, we create the ClientPortfolio extension of the displayed superclass so that it correctly loads all the properties.

The class name is specified in config , and this line is used, for example, in the factory for storage . You never download the Portfolio repository, but always download the ClientPortfolio , even if you request the repository class from the service manager under the default portfolio name.

This method can work just fine with repository functions like here (although this class is a repository for Item , not Portfolio ). I would not use one table inheritance, since you are not using multiple objects except each other. At least this is our case.

+3
source

Source: https://habr.com/ru/post/949198/


All Articles