DDC-3745: OneToOne identity through foreign entity exception on flush #4594

Closed
opened 2026-01-22 14:45:33 +01:00 by admin · 14 comments
Owner

Originally created by @doctrinebot on GitHub (May 22, 2015).

Originally assigned to: @lcobucci on GitHub.

Jira issue originally created by user Daveet:

Also asked at SO: https://stackoverflow.com/questions/30402203
I'm adding it here as well as an issue here, because I believe it just might be Doctrine's bug.


I have User and UserProfile OneToOne–related Doctrine ORM entities. They should always exist as a pair, there should be no User without UserProfile.

User should get its id from autoincrement, while UserProfile should have User's id. So they both should have the same id and there is no other column to set up the relationship (Doctrine docs: Identity through foreign Entities).

User's id is both a primary key (PK) and foreign key (FK) at the same time.

I managed to set it up, but it requires that User is saved first and only later UserProfile is created and saved in a separate step.

What I want is that UserProfile is always created with User, in the constructor, but if I do that, I get this exception:

Doctrine\ORM\ORMInvalidArgumentException: The given entity of type 'AppBundle\Entity\UserProfile' (AppBundle\Entity\UserProfile@0000000052e1b1eb00000000409c6f2c) has no identity/no id values set. It cannot be added to the identity map.

Please see code below – it works, but not the way I want. The php comments show what I want to achieve.

    /****
     * It works, both saving and loading.
     * BUT, it requires that I create and save UserProfile 
     * in a separate step than saving User step.
     */

    // create and save User
    $user = new User();
    $objectManager->persist($user);
    $objectManager->flush();

    // create and save UserProfile (this should be unnecessary)
    $user->createProfile()
    $objectManager->flush();
    use Doctrine\ORM\Mapping as ORM;

    /****
     * @ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository")
     * @ORM\Table(name="users")
     */
    class User
    {
        /****
         * @var int
         *
         * @ORM\Column(name="uid", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;

        /****
         * It's NULL at first, I create it later (after saving User).
         * 
         * @var UserProfile|null
         *
         * @ORM\OneToOne(targetEntity="UserProfile", mappedBy="user", cascade="persist")
         */
        private $profile = null;

        public function **construct()
        {
            // I want to create UserProfile inside User's constructor,
            // so that it is always present (never NULL):
            //$this->profile = new UserProfile($this);

            // but this would give me error:
            //
            // Doctrine\ORM\ORMInvalidArgumentException: 
            // The given entity of type 'AppBundle\Entity\UserProfile' 
            // (AppBundle\Entity\UserProfile@0000000058af220a0000000079dc875a)
            // has no identity/no id values set. It cannot be added to the identity map.
        }

        public function createProfile()
        {
            $this->profile = new UserProfile($this);
        }   
    }
    use Doctrine\ORM\Mapping as ORM;

    /****
     * @ORM\Entity
     * @ORM\Table(name="profiles")
     */
    class UserProfile
    {
        /****
         * – UserProfile's "uid" column points to User's "uid" column
         * – it is PK (primary key)
         * - it is FK (foreign key) as well
         * – "owning side"
         *
         * @var User
         *
         * @ORM\Id
         * @ORM\OneToOne(targetEntity="User", inversedBy="profile")
         * @ORM\JoinColumn(name="uid", referencedColumnName="uid", nullable=false)
        */
        private $user;

        public function **construct(User $user)
        {
            $this->user = $user;
        }    
    }
Originally created by @doctrinebot on GitHub (May 22, 2015). Originally assigned to: @lcobucci on GitHub. Jira issue originally created by user Daveet: Also asked at SO: https://stackoverflow.com/questions/30402203 I'm adding it here as well as an issue here, because I believe it just might be Doctrine's bug. --- I have `User` and `UserProfile` OneToOne–related Doctrine ORM entities. They should always exist as a pair, there should be no `User` without `UserProfile`. User should get its id from autoincrement, while UserProfile should have User's id. So they both should have the same id and there is no other column to set up the relationship ([Doctrine docs: Identity through foreign Entities](https://doctrine-orm.readthedocs.org/en/latest/tutorials/composite-primary-keys.html#identity-through-foreign-entities)). User's id is both a primary key (PK) and foreign key (FK) at the same time. I managed to set it up, but it requires that User is saved first and only later UserProfile is created and saved in a separate step. What I want is that UserProfile is always created with User, in the constructor, but if I do that, I get this exception: `Doctrine\ORM\ORMInvalidArgumentException: The given entity of type 'AppBundle\Entity\UserProfile' (AppBundle\Entity\UserProfile@0000000052e1b1eb00000000409c6f2c) has no identity/no id values set. It cannot be added to the identity map.` Please see code below – it works, but not the way I want. The php comments show what I want to achieve. ``` /**** * It works, both saving and loading. * BUT, it requires that I create and save UserProfile * in a separate step than saving User step. */ // create and save User $user = new User(); $objectManager->persist($user); $objectManager->flush(); // create and save UserProfile (this should be unnecessary) $user->createProfile() $objectManager->flush(); ``` ``` use Doctrine\ORM\Mapping as ORM; /**** * @ORM\Entity(repositoryClass="AppBundle\Entity\UserRepository") * @ORM\Table(name="users") */ class User { /**** * @var int * * @ORM\Column(name="uid", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /**** * It's NULL at first, I create it later (after saving User). * * @var UserProfile|null * * @ORM\OneToOne(targetEntity="UserProfile", mappedBy="user", cascade="persist") */ private $profile = null; public function **construct() { // I want to create UserProfile inside User's constructor, // so that it is always present (never NULL): //$this->profile = new UserProfile($this); // but this would give me error: // // Doctrine\ORM\ORMInvalidArgumentException: // The given entity of type 'AppBundle\Entity\UserProfile' // (AppBundle\Entity\UserProfile@0000000058af220a0000000079dc875a) // has no identity/no id values set. It cannot be added to the identity map. } public function createProfile() { $this->profile = new UserProfile($this); } } ``` ``` use Doctrine\ORM\Mapping as ORM; /**** * @ORM\Entity * @ORM\Table(name="profiles") */ class UserProfile { /**** * – UserProfile's "uid" column points to User's "uid" column * – it is PK (primary key) * - it is FK (foreign key) as well * – "owning side" * * @var User * * @ORM\Id * @ORM\OneToOne(targetEntity="User", inversedBy="profile") * @ORM\JoinColumn(name="uid", referencedColumnName="uid", nullable=false) */ private $user; public function **construct(User $user) { $this->user = $user; } } ```
admin added the Bug label 2026-01-22 14:45:33 +01:00
admin closed this issue 2026-01-22 14:45:33 +01:00
Author
Owner

@petrjirasek commented on GitHub (Feb 16, 2016):

Hello,

I would like to ask you if is there any progress? I have the same problem.

Thank you.

@petrjirasek commented on GitHub (Feb 16, 2016): Hello, I would like to ask you if is there any progress? I have the same problem. Thank you.
Author
Owner

@dm3ch commented on GitHub (Oct 5, 2016):

Hello, is there any progress with this bug? Is it fixed

@dm3ch commented on GitHub (Oct 5, 2016): Hello, is there any progress with this bug? Is it fixed
Author
Owner

@aaleksandrov commented on GitHub (Oct 25, 2016):

Same issue for me. It there any fix or workaround?

@aaleksandrov commented on GitHub (Oct 25, 2016): Same issue for me. It there any fix or workaround?
Author
Owner

@scarbo87 commented on GitHub (Nov 1, 2016):

+1

@scarbo87 commented on GitHub (Nov 1, 2016): +1
Author
Owner

@nico-incubiq commented on GitHub (Nov 9, 2016):

Same here.

@nico-incubiq commented on GitHub (Nov 9, 2016): Same here.
Author
Owner

@jorge07 commented on GitHub (Jan 27, 2017):

any fix or workaround?

@jorge07 commented on GitHub (Jan 27, 2017): any fix or workaround?
Author
Owner

@lcobucci commented on GitHub (Jan 27, 2017):

This was already fixed on #1570 when $weight was introduced on CommitOrderCalculator.

I've ran the test bellow on master and it passes with no issues, so I'm closing this issue.

<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Tests\OrmFunctionalTestCase;

class xxxxTest extends OrmFunctionalTestCase
{
    protected function setUp()
    {
        parent::setUp();

        $this->_schemaTool->createSchema(
            [
                $this->_em->getClassMetadata(xxxxUser::class),
                $this->_em->getClassMetadata(xxxxUserProfile::class),
            ]
        );
    }

    public function testThingsWorks()
    {
        $user = new xxxxUser();
        $this->_em->persist($user);
        $this->_em->flush();
    }
}

/**
 * @Entity
 */
class xxxxUser
{
    /**
     * @Id
     * @Column(name="uid", type="integer")
     * @GeneratedValue(strategy="AUTO")
     *
     * @var int
     */
    private $id;

    /**
     * @var xxxxUserProfile|null
     *
     * @OneToOne(targetEntity=xxxxUserProfile::class, mappedBy="user", cascade="persist")
     */
    private $profile = null;

    public function __construct()
    {
        $this->profile = new xxxxUserProfile($this);
    }
}
/**
 * @Entity
 */
class xxxxUserProfile
{
    /**
     * – UserProfile's "uid" column points to User's "uid" column
     * – it is PK (primary key)
     * - it is FK (foreign key) as well
     * – "owning side"
     *
     * @var xxxxUser
     *
     * @Id
     * @OneToOne(targetEntity=xxxxUser::class, inversedBy="profile")
     * @JoinColumn(name="uid", referencedColumnName="uid", nullable=false)
     */
    private $user;

    public function __construct(xxxxUser $user)
    {
        $this->user = $user;
    }
}

On the PR we have the explanation why this wasn't backported on v2.5.x.

@lcobucci commented on GitHub (Jan 27, 2017): This was already fixed on #1570 when `$weight` was introduced on `CommitOrderCalculator`. I've ran the test bellow on `master` and it passes with no issues, so I'm closing this issue. ```php <?php namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Tests\OrmFunctionalTestCase; class xxxxTest extends OrmFunctionalTestCase { protected function setUp() { parent::setUp(); $this->_schemaTool->createSchema( [ $this->_em->getClassMetadata(xxxxUser::class), $this->_em->getClassMetadata(xxxxUserProfile::class), ] ); } public function testThingsWorks() { $user = new xxxxUser(); $this->_em->persist($user); $this->_em->flush(); } } /** * @Entity */ class xxxxUser { /** * @Id * @Column(name="uid", type="integer") * @GeneratedValue(strategy="AUTO") * * @var int */ private $id; /** * @var xxxxUserProfile|null * * @OneToOne(targetEntity=xxxxUserProfile::class, mappedBy="user", cascade="persist") */ private $profile = null; public function __construct() { $this->profile = new xxxxUserProfile($this); } } /** * @Entity */ class xxxxUserProfile { /** * – UserProfile's "uid" column points to User's "uid" column * – it is PK (primary key) * - it is FK (foreign key) as well * – "owning side" * * @var xxxxUser * * @Id * @OneToOne(targetEntity=xxxxUser::class, inversedBy="profile") * @JoinColumn(name="uid", referencedColumnName="uid", nullable=false) */ private $user; public function __construct(xxxxUser $user) { $this->user = $user; } } ``` On the PR we have the explanation why this wasn't backported on `v2.5.x`.
Author
Owner

@igaponov commented on GitHub (May 22, 2017):

I have the same issue
@lcobucci Your test is wrong, I've checked it on the last version and UserProfile is not actually saved after flush, it's not passed to UOW::scheduleForInsert() as well as to UOW::addToIdentityMap(), that's why there is no exception. But if you manually persist profile before flush you'll get this:

Doctrine\ORM\ORMInvalidArgumentException: The given entity of type 'Doctrine\Tests\ORM\Functional\Ticket\xxxxUserProfile' (Doctrine\Tests\ORM\Functional\Ticket\xxxxUserProfile@0000000036941110000000004e41ba5a) has no identity/no id values set. It cannot be added to the identity map.

/home/user/projects/lib/doctrine2/lib/Doctrine/ORM/ORMInvalidArgumentException.php:68
/home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1402
/home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1214
/home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:900
/home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1664
/home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1620
/home/user/projects/lib/doctrine2/lib/Doctrine/ORM/EntityManager.php:595
/home/user/projects/lib/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/xxxxTest.php:24
<?php
namespace Doctrine\Tests\ORM\Functional\Ticket;

use Doctrine\Tests\OrmFunctionalTestCase;

class xxxxTest extends OrmFunctionalTestCase
{
    protected function setUp()
    {
        parent::setUp();

        $this->_schemaTool->createSchema(
            [
                $this->_em->getClassMetadata(xxxxUser::class),
                $this->_em->getClassMetadata(xxxxUserProfile::class),
            ]
        );
    }

    public function testThingsWorks()
    {
        $user = new xxxxUser();
        $this->_em->persist($user);
        $this->_em->persist($user->getProfile());
        $this->_em->flush();
    }
}

/**
 * @Entity
 */
class xxxxUser
{
    /**
     * @Id
     * @Column(name="uid", type="integer")
     * @GeneratedValue(strategy="AUTO")
     *
     * @var int
     */
    private $id;

    /****
     * @var xxxxUserProfile|null
     *
     * @OneToOne(targetEntity=xxxxUserProfile::class, mappedBy="user", cascade={"persist"})
     */
    private $profile = null;

    public function __construct()
    {
        $this->profile = new xxxxUserProfile($this);
    }
    
    public function getProfile() 
    {
        return $this->profile;
    }
}
/**
 * @Entity
 */
class xxxxUserProfile
{
    /**
     * – UserProfile's "uid" column points to User's "uid" column
     * – it is PK (primary key)
     * - it is FK (foreign key) as well
     * – "owning side"
     *
     * @var xxxxUser
     *
     * @Id
     * @OneToOne(targetEntity=xxxxUser::class, inversedBy="profile")
     * @JoinColumn(name="uid", referencedColumnName="uid", nullable=false)
     */
    private $user;

    public function __construct(xxxxUser $user)
    {
        $this->user = $user;
    }
}

Upd. the problem is in four asterisks in phpdoc for $profile property, doctrine doesn't see that annotation and User has no relation:

    /****    <------ wrong phpdoc
     * @var xxxxUserProfile|null
     *
     * @OneToOne(targetEntity=xxxxUserProfile::class, mappedBy="user", cascade={"persist"})
     */
    private $profile = null;
@igaponov commented on GitHub (May 22, 2017): I have the same issue @lcobucci Your test is wrong, I've checked it on the last version and UserProfile is not actually saved after flush, it's not passed to `UOW::scheduleForInsert()` as well as to `UOW::addToIdentityMap()`, that's why there is no exception. But if you manually persist profile before flush you'll get this: ``` Doctrine\ORM\ORMInvalidArgumentException: The given entity of type 'Doctrine\Tests\ORM\Functional\Ticket\xxxxUserProfile' (Doctrine\Tests\ORM\Functional\Ticket\xxxxUserProfile@0000000036941110000000004e41ba5a) has no identity/no id values set. It cannot be added to the identity map. /home/user/projects/lib/doctrine2/lib/Doctrine/ORM/ORMInvalidArgumentException.php:68 /home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1402 /home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1214 /home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:900 /home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1664 /home/user/projects/lib/doctrine2/lib/Doctrine/ORM/UnitOfWork.php:1620 /home/user/projects/lib/doctrine2/lib/Doctrine/ORM/EntityManager.php:595 /home/user/projects/lib/doctrine2/tests/Doctrine/Tests/ORM/Functional/Ticket/xxxxTest.php:24 ``` ```php <?php namespace Doctrine\Tests\ORM\Functional\Ticket; use Doctrine\Tests\OrmFunctionalTestCase; class xxxxTest extends OrmFunctionalTestCase { protected function setUp() { parent::setUp(); $this->_schemaTool->createSchema( [ $this->_em->getClassMetadata(xxxxUser::class), $this->_em->getClassMetadata(xxxxUserProfile::class), ] ); } public function testThingsWorks() { $user = new xxxxUser(); $this->_em->persist($user); $this->_em->persist($user->getProfile()); $this->_em->flush(); } } /** * @Entity */ class xxxxUser { /** * @Id * @Column(name="uid", type="integer") * @GeneratedValue(strategy="AUTO") * * @var int */ private $id; /**** * @var xxxxUserProfile|null * * @OneToOne(targetEntity=xxxxUserProfile::class, mappedBy="user", cascade={"persist"}) */ private $profile = null; public function __construct() { $this->profile = new xxxxUserProfile($this); } public function getProfile() { return $this->profile; } } /** * @Entity */ class xxxxUserProfile { /** * – UserProfile's "uid" column points to User's "uid" column * – it is PK (primary key) * - it is FK (foreign key) as well * – "owning side" * * @var xxxxUser * * @Id * @OneToOne(targetEntity=xxxxUser::class, inversedBy="profile") * @JoinColumn(name="uid", referencedColumnName="uid", nullable=false) */ private $user; public function __construct(xxxxUser $user) { $this->user = $user; } } ``` __Upd.__ the problem is in four asterisks in phpdoc for `$profile` property, doctrine doesn't see that annotation and User has no relation: ```php /**** <------ wrong phpdoc * @var xxxxUserProfile|null * * @OneToOne(targetEntity=xxxxUserProfile::class, mappedBy="user", cascade={"persist"}) */ private $profile = null; ```
Author
Owner

@lcobucci commented on GitHub (May 22, 2017):

@igaponov you're right about the docblock, now my test fails as well... and it actually fails on persist() and not flush().

IMO this is an edge-case since it only happens if xxxxUser#id is a post insert generated id (auto increment or sequences). If strategy is NONE or UUID it works just fine.

@guilhermeblanco @Ocramius I'd say this is a won't fix and we should actually document this, what do you think?

@lcobucci commented on GitHub (May 22, 2017): @igaponov you're right about the docblock, now my test fails as well... and it actually fails on `persist()` and not `flush()`. IMO this is an edge-case since it only happens if `xxxxUser#id` is a post insert generated id (auto increment or sequences). If strategy is `NONE` or `UUID` it works just fine. @guilhermeblanco @Ocramius I'd say this is a `won't fix` and we should actually document this, what do you think?
Author
Owner

@ste93cry commented on GitHub (Jan 9, 2018):

After updating to Doctrine 2.6 I get the same error while before it was working fine. I now have to use two persist and flush calls even though the main entity has the cascade option set to persist. Plus, I was relying on the fact that all INSERT queries were automatically executed in a transaction and that if any of the statements failed everything failed as well but now I have to wrap all entity operations inside a transactional function call

@ste93cry commented on GitHub (Jan 9, 2018): After updating to Doctrine 2.6 I get the same error while before it was working fine. I now have to use two `persist` and `flush` calls even though the main entity has the `cascade` option set to `persist`. Plus, I was relying on the fact that all INSERT queries were automatically executed in a transaction and that if any of the statements failed everything failed as well but now I have to wrap all entity operations inside a `transactional` function call
Author
Owner

@ro0NL commented on GitHub (Jan 9, 2018):

Im also hitting the same issue. I've identified it for now with int(1) as a value is generated anyway. WFM :) but totally unexpected yes.

@ro0NL commented on GitHub (Jan 9, 2018): Im also hitting the same issue. I've identified it for now with `int(1)` as a value is generated anyway. WFM :) but totally unexpected yes.
Author
Owner

@timostamm commented on GitHub (Jan 10, 2018):

Just followed the doctrine docs on Identity through foreign Entities and stumbled upon this bug.

When I create a new Article using the entities declared in "Use-Case 1: Dynamic Attributes", I get the mentioned exception:

$article = new Article();
$article->addAttribute("foo", "bar");
$this->em->persist($article); // throws ORMInvalidArgumentException

This was really unexpected.

@ro0NL thanks for the tip. Initializing the auto-generated id works:

class Article
{
    /** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */
    private $id = -1;

Maybe this bug should be reopened? Or mentioned in the docs?

@timostamm commented on GitHub (Jan 10, 2018): Just followed the doctrine docs on [Identity through foreign Entities](http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/composite-primary-keys.html#identity-through-foreign-entities) and stumbled upon this bug. When I create a new Article using the entities declared in "Use-Case 1: Dynamic Attributes", I get the mentioned exception: ```php $article = new Article(); $article->addAttribute("foo", "bar"); $this->em->persist($article); // throws ORMInvalidArgumentException ``` This was really unexpected. @ro0NL thanks for the tip. Initializing the auto-generated id works: ```php class Article { /** @ORM\Id @ORM\Column(type="integer") @ORM\GeneratedValue */ private $id = -1; ``` Maybe this bug should be reopened? Or mentioned in the docs?
Author
Owner

@robertonetresults commented on GitHub (Jan 16, 2018):

I have the same error too after updating to Doctrine 2.6. I can't apply the @ro0NL tip because I can't change the DB structure.

@robertonetresults commented on GitHub (Jan 16, 2018): I have the same error too after updating to Doctrine 2.6. I can't apply the @ro0NL tip because I can't change the DB structure.
Author
Owner

@NicolaF commented on GitHub (Jan 23, 2018):

For people having issues with 2.6, see #7003.

@NicolaF commented on GitHub (Jan 23, 2018): For people having issues with 2.6, see #7003.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#4594