Loading fixtures which rely on associationKey with generation fails
Closed this issue · 5 comments
Imagine the following 3 (simplified) entities:
Entity\Item:
type: entity
table: item
fields:
id:
id: true
type: integer
unsigned: true
nullable: false
generator:
strategy: IDENTITY
Entity\Product:
type: entity
table: product
fields:
id:
id: true
type: integer
unsigned: true
nullable: false
generator:
strategy: IDENTITY
Entity\ItemProduct:
type: entity
table: item_product
id:
item:
associationKey: true
product:
associationKey: true
fields:
updatedAt:
type: datetime
nullable: false
manyToOne:
item:
targetEntity: Entity\Item
oneToOne:
product:
targetEntity: Entity\Product
and the following fixture file:
Entity\Item:
item{1..10}:
itemName: <sentence()>
updatedAt: <dateTimeBetween('-1 year', 'now')>
Entity\Product:
product{1..20}:
productSku: <bothify('?????????##')>
updatedAt: <dateTimeBetween('-1 year', 'now')>
Entity\ItemProduct:
itemProduct{1..40}:
item: @item<numberBetween(1, 10)>
product: @product<numberBetween(1, 20)>
updatedAt: <dateTimeBetween('-1 year', 'now')>
When trying to load this It will always fail with an error such as:
[Doctrine\ORM\ORMException]
Entity of type Entity\ItemProduct has identity through a foreign entity Entity\Item,
however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before
trying to persist 'Entity\ItemProduct'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SER
IAL) this means you have to call EntityManager#flush() between both persist operations.
Since the id for Item and Product is generated by the database if you try to persist the entity with a primary key formed by association keys it will fail since it's not able to find an id to use when persisting.
I kind of "hacked" this behaviour by catching this exception an issuing a flush then but this is far from being the right solution.
I also tried separating the fixture into files doing first Product and Item and then ItemProduct but this way you can't keep the fanciness of using things such as @item<numberBetween(1, 10)>
when creating ItemProduct entities.
Another way will be to add a flag to force a flush between files, not the perfect one but it will alleviate the issue. Maybe I'm missing something else here but I'm not able to find a way to do this in a proper way 😞
@garciall Thanks for your contribution and the superb explanation.
The problem occured because of the ORM detach(), which will lead to unknown references in Doctrine ArrayCollections. This call be removed in the next release.
A workaround could be using cascade={persist}: http://doctrine-orm.readthedocs.org/en/latest/reference/working-with-associations.html#transitive-persistence-cascade-operations
Hi @h4cc,
I seem to be experiencing the same issue with version: 0.5.1. cascade persist doesn't help. Is there other way around this issue?
@h4cc I have almost the same relation as garciall (different table names).
I have resolved it by loading main tables first (game , publisher) & then n:m relations (gamepublisher) in different set.
\ZapTvBundle\Entity\Game:
content_game{1..25}:
originalTitle: <word()>
description: <paragraph(8)>
\ZapTvBundle\Entity\Publisher:
publisher{1..10}:
name: <word()>
type: <randomElement(array(1, 2))>
\ZapTvBundle\Entity\GamePublisher:
game_publishers_world{1..25}:
game: @content_game*
publisher: @publisher*
I have resolved it by loading main tables first (game , publisher) & then n:m relations (gamepublisher) in different set.
That should be the recommended way.
Its not a problem with loading/creating the fixtures, its about persisting in doctrine in correct order, which can not be known to alice.