Persist problem with "Entity has identity through a foreign entity" #5725

Closed
opened 2026-01-22 15:15:40 +01:00 by admin · 4 comments
Owner

Originally created by @va5ja on GitHub (Oct 3, 2017).

Originally assigned to: @Ocramius on GitHub.

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.

Originally created by @va5ja on GitHub (Oct 3, 2017). Originally assigned to: @Ocramius on GitHub. 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. ```yaml ...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: ```yaml ...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: ```php $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: ```yaml ...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: ```php $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.
admin added the BugDuplicate labels 2026-01-22 15:15:40 +01:00
admin closed this issue 2026-01-22 15:15:41 +01:00
Author
Owner

@lcobucci commented on GitHub (Nov 26, 2017):

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).

@lcobucci commented on GitHub (Nov 26, 2017): 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).
Author
Owner

@Ocramius commented on GitHub (Feb 27, 2018):

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

@Ocramius commented on GitHub (Feb 27, 2018): @va5ja please verify if this is actually a duplicate - we merged all related changes and will release `2.6.1` with them.
Author
Owner

@va5ja commented on GitHub (Mar 1, 2018):

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 commented on GitHub (Mar 1, 2018): 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.
Author
Owner

@Ocramius commented on GitHub (Mar 1, 2018):

@va5ja thanks!

@Ocramius commented on GitHub (Mar 1, 2018): @va5ja thanks!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#5725