I have the following entity:
class Restaurant
{
/**
* @OneToMany(targetEntity="CollectionTime", mappedBy="restaurant")
*/
protected $collectionTimes;
/**
* @OneToMany(targetEntity="DeliveryTime", mappedBy="restaurant")
*/
protected $deliveryTimes;
}
Mapping to two subclasses of the same entity:
/**
* @Entity
* @InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorMap({
* "CollectionTime" = "CollectionTime",
* "DeliveryTime" = "DeliveryTime"
* })
*/
abstract class OrderTime
{
/**
* @ManyToOne(targetEntity="Restaurant")
*/
protected $restaurant;
}
/**
* @Entity
*/
class CollectionTime extends OrderTime
{
}
/**
* @Entity
*/
class DeliveryTime extends OrderTime
{
}
Now the problem is, doctrine orm:validate-schema
reports the following errors:
The field Restaurant#collectionTimes is on the inverse side of a bi-directional relationship, but the specified mappedBy association on the target-entity CollectionTime#restaurant does not contain the required 'inversedBy=collectionTimes' attribute.
The field Restaurant#deliveryTimes is on the inverse side of a bi-directional relationship, but the specified mappedBy association on the target-entity DeliveryTime#restaurant does not contain the required 'inversedBy=deliveryTimes' attribute.
In short, Doctrine expects every mappedBy
to have an inversedBy
on the other side.
The only solution I can see so far is to move the OrderTime::$restaurant
property and mapping to CollectionTime
and DeliveryTime
, just to be able to add the proper inversedBy
mapping:
abstract class OrderTime
{
}
/**
* @Entity
*/
class CollectionTime extends OrderTime
{
/**
* @ManyToOne(targetEntity="Restaurant", inversedBy="collectionTimes")
*/
protected $restaurant;
}
/**
* @Entity
*/
class DeliveryTime extends OrderTime
{
/**
* @ManyToOne(targetEntity="Restaurant", inversedBy="deliveryTimes")
*/
protected $restaurant;
}
But it is cumbersome and goes against the principle of inheritance.
Is there a way to just override the inversedBy
attribute in the subclasses, without having to (re)declare the whole property in the subclass?
I've looked into @AssociationOverrides and @AttributeOverrides, but they don't seem to be designed for this purpose.
You can override
inversedBy
since Doctrine 2.6 (released a few days ago). That would look like that:Unidirectional / bidirectional
Doctrine 2 uses the concept of unidirectional associations and bidirectional associations.
When defining a unidirectional association you have to omit the
inversedBy
parameter, because the association isn't inversed.When defining a bidirectional association you have to use the
inversedBy
parameter on the owning side (that's the side that actually contains all metadata for Doctrine to properly map the association), and you have to use themappedBy
parameter on the inversed side (so that Doctrine knows where to look for the actual mapping metadata for the association).So you either use both
inversedBy
andmappedBy
(bidirectional) or you don't use them at all (unidirectional).Principle of inheritance
I think that depends on how you look at it:
If you only look at the code (not the mapping), you are correct. Both concrete implementations of
OrderTime
share a property$restaurant
(and probably getters, setters, and maybe other logic), so the principle dictates that you define that inOrderTime
.But when you look at the mapping, you have 2 different associations: One that ties
Restaurant::$collectionTimes
andCollectionTime::$restaurant
together, and one that tiesRestaurant::$deliveryTimes
andDeliveryTime::$restaurant
together.Because these are 2 different associations, it's only fair that Doctrine wants you to properly define them both.
You can still stick to principle of inheritance in the following way: Define all shared logic you need in
OrderTime
, even the property$restaurant
, just don't add the mapping metadata. In the concrete implementations you can redeclare the property$restaurant
with the proper mapping metadata.The only reason you have to redeclare the property
$restaurant
in those concretes is that you're using Annotations for mapping metadata. When using Yaml or XML, you don't have to redeclare the property because the mapping metadata will be in separate files.So in fact it's not code/logic you're defining in those concrete implementations, it's the mapping metadata.
Value Object
Those
OrderTime
classes look more like Value Objects to me (not Entities): A small simple object, like money or a date range, whose equality isn't based on identity.Doctrine will support Value Objects starting with version 2.5 (see here).