Skip to content

Commit 4672c87

Browse files
committed
fix(autorefresh): don't use clone to get the id values
1 parent 5d02ac6 commit 4672c87

File tree

6 files changed

+66
-10
lines changed

6 files changed

+66
-10
lines changed

src/Configuration.php

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,17 @@ public function inADataProvider(): bool
111111
return $this->bootedForDataProvider;
112112
}
113113

114-
public static function instance(): self
115-
{
114+
public static function instance(
115+
/** @internal */
116+
bool $skipCheckFactoriesTrait = false,
117+
): self {
116118
if (!self::$instance) {
117119
throw new FoundryNotBooted();
118120
}
119121

120-
FactoriesTraitNotUsed::throwIfComingFromKernelTestCaseWithoutFactoriesTrait();
122+
if (!$skipCheckFactoriesTrait) {
123+
FactoriesTraitNotUsed::throwIfComingFromKernelTestCaseWithoutFactoriesTrait();
124+
}
121125

122126
return \is_callable(self::$instance) ? (self::$instance)() : self::$instance;
123127
}

src/Persistence/PersistenceManager.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,16 @@ public function flush(ObjectManager $om): void
148148
*
149149
* @param T $object
150150
*/
151-
public function autorefresh(object $object, object $clone): void
151+
public function autorefresh(object $object, mixed $id, object $clone): void
152152
{
153153
$strategy = $this->strategyFor($object::class);
154154
$om = $strategy->objectManagerFor($object::class);
155155

156-
$id = $strategy->getIdentifierValues($clone);
157-
158156
if ($id) {
159157
try {
160158
$om->refresh($object);
161159

162-
if ($strategy->getIdentifierValues($object)) {
160+
if ($this->getIdentifierValues($object)) {
163161
// no identifier values means the object no longer exists
164162
return;
165163
}
@@ -398,6 +396,11 @@ public function resetDatabaseManager(): ResetDatabaseManager
398396
return $this->resetDatabaseManager;
399397
}
400398

399+
public function getIdentifierValues(object $object): mixed
400+
{
401+
return $this->strategyFor($object::class)->getIdentifierValues($object);
402+
}
403+
401404
public static function isOrmOnly(): bool
402405
{
403406
static $isOrmOnly = null;

src/Persistence/Proxy/PersistedObjectsTracker.php

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ static function(\WeakReference $weakRef) {
6262
return \WeakReference::create($object);
6363
}
6464

65+
$id = Configuration::instance(skipCheckFactoriesTrait: true)->persistence()->getIdentifierValues($object);
6566
$clone = clone $object;
66-
$reflector->resetAsLazyGhost($object, function($object) use ($clone) {
67-
Configuration::instance()->persistence()->autorefresh($object, $clone);
67+
$reflector->resetAsLazyGhost($object, function($object) use ($id, $clone) {
68+
Configuration::instance()->persistence()->autorefresh($object, $id, $clone);
6869
});
6970

7071
return \WeakReference::create($object);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Zenstruck\Foundry\Tests\Fixture\Entity\EdgeCases;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
#[ORM\Entity]
8+
#[ORM\Table('entity_with_clone_method')]
9+
class EntityWithCloneMethod
10+
{
11+
#[ORM\Id]
12+
#[ORM\GeneratedValue]
13+
#[ORM\Column(type: 'integer')]
14+
public int|null $id = null;
15+
16+
#[ORM\Column(nullable: true)]
17+
public string|null $prop = null;
18+
19+
public function __clone()
20+
{
21+
$this->id = null;
22+
}
23+
}

tests/Integration/ORM/AutoRefreshTest.php

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@
2626
use Zenstruck\Foundry\Tests\Fixture\DoctrineCascadeRelationship\ChangesEntityRelationshipCascadePersist;
2727
use Zenstruck\Foundry\Tests\Fixture\DoctrineCascadeRelationship\UsingRelationships;
2828
use Zenstruck\Foundry\Tests\Fixture\Entity\Contact;
29+
use Zenstruck\Foundry\Tests\Fixture\Entity\EdgeCases\EntityWithCloneMethod;
2930
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Address\AddressFactory;
3031
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Category\CategoryFactory;
3132
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;
3233
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\GenericEntityFactory;
3334
use Zenstruck\Foundry\Tests\Integration\Persistence\AutoRefreshTestCase;
3435
use Zenstruck\Foundry\Tests\Integration\RequiresORM;
3536

37+
use function Zenstruck\Foundry\Persistence\persistent_factory;
3638
use function Zenstruck\Foundry\Persistence\refresh_all;
3739

3840
/**
@@ -123,7 +125,6 @@ public function it_can_refresh_objects_with_relationships(): void
123125
}
124126

125127
#[Test]
126-
#[IgnorePhpunitWarnings(EdgeCasesRelationshipTest::DATA_PROVIDER_WARNING_REGEX)]
127128
public function it_can_refresh_with_doctrine_proxies(): void
128129
{
129130
$contact = ContactFactory::createOne();
@@ -151,6 +152,22 @@ public function it_can_refresh_with_doctrine_proxies(): void
151152
self::assertSame('foo', $category->getName());
152153
}
153154

155+
#[Test]
156+
public function it_can_refresh_entity_which_removes_its_id_in_clone(): void
157+
{
158+
$entity = persistent_factory(EntityWithCloneMethod::class)->create();
159+
$entityId = $entity->id;
160+
161+
$this->objectManager()->getConnection()->executeQuery('UPDATE entity_with_clone_method SET prop = \'foo\' WHERE id = ?', [$entity->id]);
162+
163+
self::ensureKernelShutdown();
164+
165+
self::assertTrue((new \ReflectionClass($entity))->isUninitializedLazyObject($entity));
166+
167+
self::assertSame('foo', $entity->prop);
168+
self::assertSame($entityId, $entity->id);
169+
}
170+
154171
protected static function factory(): PersistentObjectFactory
155172
{
156173
return GenericEntityFactory::new();

tests/Integration/Persistence/AutoRefreshTestCase.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public function it_can_refresh_after_kernel_shutdown(): void
6262

6363
// service reset did clear the EM, thus the object is not managed anymore
6464
self::assertFalse($this->objectManager()->contains($object));
65+
self::assertSame($objectId, $object->id);
6566
}
6667

6768
#[Test]
@@ -80,6 +81,9 @@ public function it_can_refresh_many_objects(): void
8081

8182
self::assertSame('foo', $object1->getProp1());
8283
self::assertSame('foo', $object2->getProp1());
84+
85+
self::assertSame($objectId1, $object1->id);
86+
self::assertSame($objectId2, $object2->id);
8387
}
8488

8589
#[Test]
@@ -88,6 +92,8 @@ public function it_can_refresh_after_update_with_browser(): void
8892
$client = self::createClient();
8993

9094
$object = $this->factory()->create();
95+
$objectId = $object->id;
96+
9197
self::assertSame('default1', $object->getProp1());
9298
self::assertFalse((new \ReflectionClass($object))->isUninitializedLazyObject($object));
9399

@@ -100,6 +106,8 @@ public function it_can_refresh_after_update_with_browser(): void
100106
assert_persisted($object);
101107
self::assertSame('foo', $object->getProp1());
102108
self::assertFalse((new \ReflectionClass($object))->isUninitializedLazyObject($object));
109+
110+
self::assertSame($objectId, $object->id);
103111
}
104112

105113
#[Test]

0 commit comments

Comments
 (0)