Skip to content

Commit fb5516a

Browse files
committed
fix: should not use flush_after() in FactoryCollection::create()
1 parent 40ce8a2 commit fb5516a

File tree

4 files changed

+63
-22
lines changed

4 files changed

+63
-22
lines changed

src/FactoryCollection.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@
1414
use Zenstruck\Foundry\Persistence\PersistentObjectFactory;
1515
use Zenstruck\Foundry\Persistence\PersistMode;
1616

17-
use function Zenstruck\Foundry\Persistence\flush_after;
18-
1917
/**
2018
* @author Kevin Bond <[email protected]>
2119
*
@@ -147,9 +145,12 @@ public static function sequence(Factory $factory, iterable $items): self
147145
public function create(array|callable $attributes = []): array
148146
{
149147
if (Configuration::instance()->flushOnce && $this->isRootFactory && $this->factory instanceof PersistentObjectFactory && $this->factory->isPersisting()) {
150-
return flush_after(
151-
fn() => \array_map(static fn(Factory $f) => $f->create($attributes), $this->all())
152-
);
148+
// @phpstan-ignore argument.type (if "$this->factory instanceof PersistentObjectFactory", $this->all() returns a list of PersistentObjectFactory)
149+
$objects = \array_map(static fn(PersistentObjectFactory $f) => $f->notRootFactory()->create($attributes), $this->all());
150+
151+
Configuration::instance()->persistence()->save(...$objects);
152+
153+
return $objects;
153154
}
154155

155156
return \array_map(static fn(Factory $f) => $f->create($attributes), $this->all());

src/Persistence/PersistenceManager.php

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,30 +59,29 @@ public function enablePersisting(): void
5959
$this->persist = true;
6060
}
6161

62-
/**
63-
* @template T of object
64-
*
65-
* @param T $object
66-
*
67-
* @return T
68-
*/
69-
public function save(object $object): object
62+
public function save(object $object, object ...$objects): void
7063
{
71-
if ($object instanceof Proxy) {
72-
return $object->_save();
64+
$objects = unproxy([$object, ...$objects]);
65+
66+
$oms = [];
67+
foreach ($objects as $object) {
68+
$om = $this->strategyFor($object::class)->objectManagerFor($object::class);
69+
$om->persist($object);
70+
71+
$oms[\spl_object_id($om)] = $om;
7372
}
7473

75-
$om = $this->strategyFor($object::class)->objectManagerFor($object::class);
76-
$om->persist($object);
77-
$this->flush($om);
74+
foreach ($oms as $om) {
75+
$this->flush($om);
76+
}
7877

7978
$callbacksCalled = $this->callPostPersistCallbacks();
8079

8180
if ($callbacksCalled) {
82-
$this->flush($om);
81+
foreach ($oms as $om) {
82+
$this->flush($om);
83+
}
8384
}
84-
85-
return $object;
8685
}
8786

8887
/**

src/Persistence/functions.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,9 @@ function unproxy(mixed $what, bool $withAutoRefresh = true): mixed
121121
*/
122122
function save(object $object): object
123123
{
124-
return Configuration::instance()->persistence()->save($object);
124+
Configuration::instance()->persistence()->save($object);
125+
126+
return $object;
125127
}
126128

127129
/**

tests/Integration/ORM/EntityRelationship/EntityFactoryRelationshipTestCase.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\Contact\ContactFactory;
3535

3636
use function Zenstruck\Foundry\lazy;
37+
use function Zenstruck\Foundry\Persistence\flush_after;
3738
use function Zenstruck\Foundry\Persistence\refresh;
3839
use function Zenstruck\Foundry\Persistence\unproxy;
3940

@@ -712,6 +713,44 @@ static function(Address $a): void {
712713
self::assertNotNull($address->getContact());
713714
}
714715

716+
/** @test */
717+
#[Test]
718+
#[DataProvider('provideCascadeRelationshipsCombinations')]
719+
#[UsingRelationships(Contact::class, ['category'])]
720+
public function find_or_create_used_in_many_actually_create_only_one_object(): void
721+
{
722+
static::contactFactory()
723+
->many(2)
724+
->create(['category' => lazy(fn() => static::categoryFactory()::findOrCreate([]))]);
725+
726+
static::contactFactory()::assert()->count(2);
727+
static::categoryFactory()::assert()->count(1);
728+
}
729+
730+
/** @test */
731+
#[Test]
732+
#[DataProvider('provideCascadeRelationshipsCombinations')]
733+
#[UsingRelationships(Contact::class, ['category'])]
734+
public function ensure_inverse_one_to_many_is_hydrated_before_persisting_in_flush_after(): void
735+
{
736+
flush_after(
737+
static function() {
738+
$contactFactory = static::contactFactory()
739+
->afterPersist(static function(Contact $contact) {
740+
self::assertNotNull($contact->getCategory());
741+
});
742+
743+
static::categoryFactory()::createMany(
744+
2,
745+
['contacts' => lazy(static fn() => $contactFactory->many(2)->create(['category' => null]))]
746+
);
747+
}
748+
);
749+
750+
static::contactFactory()::assert()->count(4);
751+
static::categoryFactory()::assert()->count(2);
752+
}
753+
715754
/** @return PersistentObjectFactory<Contact> */
716755
protected static function contactFactoryWithoutCategory(): PersistentObjectFactory
717756
{

0 commit comments

Comments
 (0)