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!
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:
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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With