Bug Report
Summary
Using entity (with inheritance) as ID (by OneToOne) throw EntityIdentityCollisionException
BusinessCase:
id: int
contract: Contract (bidirectional OneToOne)
Contract:
id: BusinessCase (bidirectional OneToOne)
Attachment:
id: int
contract: Contract
$entityManager->find(Attachment::class, 1); // throw Exception
Current behavior
Doctrine\ORM\Exception\EntityIdentityCollisionException: While adding an entity of class Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\ORM\Functional\Ticket\GH12428Contract with an ID hash of "1" to the identity map,
another object of class Doctrine\Tests\ORM\Functional\Ticket\GH12428Contract was already present for the same ID. This exception
is a safeguard against an internal inconsistency - IDs should uniquely map to
entity object instances. This problem may occur if:
- you use application-provided IDs and reuse ID values;
- database-provided IDs are reassigned after truncating the database without
clearing the EntityManager;
- you might have been using EntityManager#getReference() to create a reference
for a nonexistent ID that was subsequently (by the RDBMS) assigned to another
entity.
Expected behavior
Don't allow add entity to identity map twice.
How to reproduce
<?php
declare(strict_types=1);
namespace Doctrine\Tests\ORM\Functional\Ticket;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Tests\OrmFunctionalTestCase;
use PHPUnit\Framework\Attributes\Group;
/** @see https://github.com/doctrine/orm/issues/12428 */
#[Group('GH12428')]
class GH12428Test extends OrmFunctionalTestCase
{
protected function setUp(): void
{
parent::setUp();
$this->createSchemaForModels(
GH12428BusinessCase::class,
GH12428Contract::class,
GH12428Attachment::class,
);
}
public function testIssue(): void
{
$businessCase = new GH12428ABusinessCase();
$attachment = new GH12428Attachment($businessCase->contract);
$this->_em->persist($businessCase);
$this->_em->persist($attachment);
$this->_em->flush();
$this->_em->clear();
$loadedAttachment = $this->_em->find(GH12428Attachment::class, $attachment->id);
self::assertNotNull($loadedAttachment);
}
}
#[ORM\Entity]
#[ORM\InheritanceType('SINGLE_TABLE')]
#[ORM\DiscriminatorMap([
'A' => GH12428ABusinessCase::class,
])]
abstract class GH12428BusinessCase
{
#[ORM\Id]
#[ORM\Column]
#[ORM\GeneratedValue]
public int|null $id = null;
#[ORM\OneToOne(mappedBy: 'id', cascade: ['persist'])]
public GH12428Contract $contract;
public function __construct()
{
$this->contract = new GH12428Contract($this);
}
}
#[ORM\Entity]
class GH12428ABusinessCase extends GH12428BusinessCase
{
}
#[ORM\Entity]
class GH12428Attachment
{
#[ORM\Id]
#[ORM\Column]
#[ORM\GeneratedValue]
public int|null $id = null;
public function __construct(
#[ORM\ManyToOne]
public GH12428Contract $contract,
) {
}
}
#[ORM\Entity]
class GH12428Contract
{
public function __construct(
#[ORM\Id]
#[ORM\OneToOne(inversedBy: 'contract')]
#[ORM\JoinColumn(name: 'id')]
public GH12428BusinessCase $id,
) {
}
}
Test case: janatjak@0dae9be
Result: https://github.com/janatjak/orm/actions/runs/24121411601/job/70376040902
Bug Report
Summary
Using entity (with inheritance) as ID (by OneToOne) throw EntityIdentityCollisionException
Current behavior
Expected behavior
Don't allow add entity to identity map twice.
How to reproduce
Test case: janatjak@0dae9be
Result: https://github.com/janatjak/orm/actions/runs/24121411601/job/70376040902