Skip to content

Commit 04694a9

Browse files
authored
Merge pull request #11835 from gseidel/fix-pre-persist-call-persist
fix: calling scheduleForInsert twice
2 parents 39e35fc + 3aaaf37 commit 04694a9

File tree

2 files changed

+115
-1
lines changed

2 files changed

+115
-1
lines changed

src/UnitOfWork.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,9 @@ private function persistNew(ClassMetadata $class, $entity): void
10601060

10611061
$this->entityStates[$oid] = self::STATE_MANAGED;
10621062

1063-
$this->scheduleForInsert($entity);
1063+
if (! isset($this->entityInsertions[$oid])) {
1064+
$this->scheduleForInsert($entity);
1065+
}
10641066
}
10651067

10661068
/** @param mixed[] $idValue */
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Doctrine\Tests\ORM\Functional;
6+
7+
use Doctrine\ORM\Event\PrePersistEventArgs;
8+
use Doctrine\ORM\Events;
9+
use Doctrine\ORM\Mapping\Column;
10+
use Doctrine\ORM\Mapping\Entity;
11+
use Doctrine\ORM\Mapping\GeneratedValue;
12+
use Doctrine\ORM\Mapping\Id;
13+
use Doctrine\ORM\Mapping\ManyToOne;
14+
use Doctrine\Tests\OrmFunctionalTestCase;
15+
16+
use function uniqid;
17+
18+
class PrePersistEventTest extends OrmFunctionalTestCase
19+
{
20+
protected function setUp(): void
21+
{
22+
parent::setUp();
23+
24+
$this->createSchemaForModels(
25+
EntityWithUnmappedEntity::class,
26+
EntityWithCascadeAssociation::class
27+
);
28+
}
29+
30+
public function testCallingPersistInPrePersistHook(): void
31+
{
32+
$entityWithUnmapped = new EntityWithUnmappedEntity();
33+
$entityWithCascade = new EntityWithCascadeAssociation();
34+
35+
$entityWithUnmapped->unmapped = $entityWithCascade;
36+
$entityWithCascade->cascaded = $entityWithUnmapped;
37+
38+
$this->_em->getEventManager()->addEventListener(Events::prePersist, new PrePersistUnmappedPersistListener());
39+
$this->_em->persist($entityWithUnmapped);
40+
41+
$this->assertTrue($this->_em->getUnitOfWork()->isScheduledForInsert($entityWithCascade));
42+
$this->assertTrue($this->_em->getUnitOfWork()->isScheduledForInsert($entityWithUnmapped));
43+
}
44+
}
45+
46+
class PrePersistUnmappedPersistListener
47+
{
48+
public function prePersist(PrePersistEventArgs $args): void
49+
{
50+
$object = $args->getObject();
51+
52+
if ($object instanceof EntityWithUnmappedEntity) {
53+
$uow = $args->getObjectManager()->getUnitOfWork();
54+
55+
if ($object->unmapped && ! $uow->isInIdentityMap($object->unmapped) && ! $uow->isScheduledForInsert($object->unmapped)) {
56+
$args->getObjectManager()->persist($object->unmapped);
57+
}
58+
}
59+
}
60+
}
61+
62+
/** @Entity */
63+
#[Entity]
64+
class EntityWithUnmappedEntity
65+
{
66+
/**
67+
* @var string
68+
* @Id
69+
* @Column(type="string", length=255)
70+
* @GeneratedValue(strategy="NONE")
71+
*/
72+
#[Id]
73+
#[Column(type: 'string', length: 255)]
74+
#[GeneratedValue(strategy: 'NONE')]
75+
public $id;
76+
77+
/** @var ?EntityWithCascadeAssociation */
78+
public $unmapped = null;
79+
80+
public function __construct()
81+
{
82+
$this->id = uniqid(self::class, true);
83+
}
84+
}
85+
86+
/** @Entity */
87+
#[Entity]
88+
class EntityWithCascadeAssociation
89+
{
90+
/**
91+
* @var string
92+
* @Id
93+
* @Column(type="string", length=255)
94+
* @GeneratedValue(strategy="NONE")
95+
*/
96+
#[Id]
97+
#[Column(type: 'string', length: 255)]
98+
#[GeneratedValue(strategy: 'NONE')]
99+
public $id;
100+
101+
/**
102+
* @var ?EntityWithUnmappedEntity
103+
* @ManyToOne(targetEntity=EntityWithUnmappedEntity::class, cascade={"persist"})
104+
*/
105+
#[ManyToOne(targetEntity: EntityWithUnmappedEntity::class, cascade: ['persist'])]
106+
public $cascaded = null;
107+
108+
public function __construct()
109+
{
110+
$this->id = uniqid(self::class, true);
111+
}
112+
}

0 commit comments

Comments
 (0)