Orm: Persist problem with "Entity has identity through a foreign entity"

Created on 3 Oct 2017  路  4Comments  路  Source: doctrine/orm

Hi guys,

I'm having a hard time understanding why Doctrine (version 2.5.3) won't handle the following schemas as expected. I made two example entities Test and TestTranslated. In the first example the TestTranslated table has a dedicated primary key and in the second example the TestTranslated entity has a compound primary key build out of two foreign keys.

...Bundle\Entity\Test:
    type: entity
    table: test
    id:
        id:
            type: integer
            nullable: false
            id: true
            generator:
                strategy: AUTO
    fields:
        datetime:
            type: datetime
            nullable: false
        city:
            type: string
            nullable: false
            length: 45
            fixed: false
    oneToMany:
        testTranslation:
            targetEntity: TestTranslation
            mappedBy: test
            indexBy: language_id
            cascade: ["persist"]

Example1:

...Bundle\Entity\TestTranslation:
    type: entity
    table: testtranslation
    id:
        id:
            type: integer
            nullable: false
            id: true
            generator:
                strategy: AUTO
    fields:
        location:
            type: text
            nullable: true
            length: 255
            fixed: false
        metaTitle:
            type: string
            nullable: true
            length: 75
            fixed: false
            column: meta_title
        metaDescription:
            type: string
            nullable: true
            length: 210
            fixed: false
            column: meta_description
        metaKeyword:
            type: string
            nullable: true
            length: 150
            fixed: false
            column: meta_keyword
        slug:
            type: string
            nullable: false
            length: 255
            fixed: false
    manyToOne:
        test:
            targetEntity: Test
            joinColumns:
                test_id:
                    referencedColumnName: id
        language:
            targetEntity: Language
            joinColumns:
                language_id:
                    referencedColumnName: id

In this case everything works as expected. Running the code:

$em = $this->getDoctrine()->getEntityManager();

$a = new Test();
$a->setCity('Berlin');
$a->setDatetime(new \DateTime());

$c = $em->getRepository(Language::class)->findOneById('en');
$b = new TestTranslation();
$b->setTest($a);
$b->setLanguage($c);
$b->setLocation('Ljubljana');
$b->setMetaDescription('Meta description');
$b->setMetaKeyword('Meta keyword');
$b->setMetaTitle('Meta title');
$b->setSlug('ljubljana');

$a->addTestTranslation($b);

$em->persist($a);
$em->flush();

I get MySQL entries in both Test and TestTranslated entities with one flush. So all good.

Example2:

...Bundle\Entity\TestTranslation:
    type: entity
    table: testtranslation
    id:
        test:
            associationKey: true
        language:
            associationKey: true
    fields:
        location:
            type: text
            nullable: true
            length: 255
            fixed: false
        metaTitle:
            type: string
            nullable: true
            length: 75
            fixed: false
            column: meta_title
        metaDescription:
            type: string
            nullable: true
            length: 210
            fixed: false
            column: meta_description
        metaKeyword:
            type: string
            nullable: true
            length: 150
            fixed: false
            column: meta_keyword
        slug:
            type: string
            nullable: false
            length: 255
            fixed: false
    manyToOne:
        test:
            targetEntity: Test
            inversedBy: testTranslation
            joinColumns:
                test_id:
                    referencedColumnName: id
                    nullable: false
        language:
            targetEntity: Language
            inversedBy: testTranslation
            joinColumns:
                language_id:
                    referencedColumnName: id
                    nullable: false

In this case I get an error:
Entity of type ...Bundle\Entity\TestTranslation has identity through a foreign entity ...Bundle\Entity\Test, 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 '...Bundle\Entity\TestTranslation'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.

What I can do is run the code in the following way:

$em = $this->getDoctrine()->getEntityManager();

$a = new Test();
$a->setCity('Berlin');
$a->setDatetime(new \DateTime());

$em->persist($a);
$em->flush();

$c = $em->getRepository(Language::class)->findOneById('en');
$b = new TestTranslation();
$b->setTest($a);
$b->setLanguage($c);
$b->setLocation('Ljubljana');
$b->setMetaDescription('Meta description');
$b->setMetaKeyword('Meta keyword');
$b->setMetaTitle('Meta title');
$b->setSlug('ljubljana');

$a->addTestTranslation($b);

$em->flush();

but I don't understand why in this case Doctrine isn't that smart to deal with insert sequence. Meaning that it would first save Test entity and then TestTranslated. It works as expected with Example1 but not with Example2 although there's almost no difference in table schema. The only difference is the compound primary key.

I personally don't see any reason why Doctrine wouldn't act the same. Is there a reason behind this behavior or is it just expected that every table has a dedicated primary key? I prefer having a compound primary key.

Bug Duplicate

All 4 comments

This seems to be related to https://github.com/doctrine/doctrine2/pull/6701 (and duplicates #6531 and #6043).

@va5ja you can use a pre-generated value as ID in order to solve this for now (UUID, for example).

@va5ja please verify if this is actually a duplicate - we merged all related changes and will release 2.6.1 with them.

I don't get the above error anymore. Both examples work fine so obviously the problem has been fixed and this issue is a duplicate.

@va5ja thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

doctrinebot picture doctrinebot  路  3Comments

doctrinebot picture doctrinebot  路  4Comments

podorozhny picture podorozhny  路  4Comments

goatfryed picture goatfryed  路  3Comments

doctrinebot picture doctrinebot  路  3Comments