Skip to content

Commit b9d2ed3

Browse files
authored
[feature] add Factory::delayFlush() (#84)
1 parent 8a7437a commit b9d2ed3

File tree

6 files changed

+104
-2
lines changed

6 files changed

+104
-2
lines changed

docs/index.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,22 @@ Foundry can be used to create factories for entities that you don't have model f
865865
$entity = create(Post::class, ['field' => 'value']);
866866
$entities = create_many(Post::class, 5, ['field' => 'value']);
867867
868+
Delay Flush
869+
~~~~~~~~~~~
870+
871+
When creating/persisting many factories at once, it can be improve performance
872+
to instantiate them all without saving to the database, then flush them all at
873+
once. To do this, wrap the operations in a ``Factory::delayFlush()`` callback:
874+
875+
.. code-block:: php
876+
877+
use Zenstruck\Foundry\Factory;
878+
879+
Factory::delayFlush(function() {
880+
CategoryFactory::createMany(100); // instantiated/persisted but not flushed
881+
TagFactory::createMany(200); // instantiated/persisted but not flushed
882+
}); // single flush
883+
868884
.. _without-persisting:
869885

870886
Without Persisting

src/Configuration.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ final class Configuration
3232
/** @var bool|null */
3333
private $defaultProxyAutoRefresh;
3434

35+
/** @var bool */
36+
private $flushEnabled = true;
37+
3538
public function __construct()
3639
{
3740
$this->stories = new StoryManager([]);
@@ -124,6 +127,24 @@ public function disableDefaultProxyAutoRefresh(): self
124127
return $this;
125128
}
126129

130+
public function isFlushingEnabled(): bool
131+
{
132+
return $this->flushEnabled;
133+
}
134+
135+
public function delayFlush(callable $callback): void
136+
{
137+
$this->flushEnabled = false;
138+
139+
$callback();
140+
141+
foreach ($this->managerRegistry()->getManagers() as $manager) {
142+
$manager->flush();
143+
}
144+
145+
$this->flushEnabled = true;
146+
}
147+
127148
/**
128149
* @param object|string $objectOrClass
129150
*

src/Factory.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ final public static function faker(): Faker\Generator
255255
return self::configuration()->faker();
256256
}
257257

258+
final public static function delayFlush(callable $callback): void
259+
{
260+
self::configuration()->delayFlush($callback);
261+
}
262+
258263
/**
259264
* @internal
260265
*

src/Proxy.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public function isPersisted(): bool
110110
*/
111111
public function object(): object
112112
{
113-
if (!$this->autoRefresh || !$this->persisted) {
113+
if (!$this->autoRefresh || !$this->persisted || !Factory::configuration()->isFlushingEnabled()) {
114114
return $this->object;
115115
}
116116

@@ -139,7 +139,11 @@ public function object(): object
139139
public function save(): self
140140
{
141141
$this->objectManager()->persist($this->object);
142-
$this->objectManager()->flush();
142+
143+
if (Factory::configuration()->isFlushingEnabled()) {
144+
$this->objectManager()->flush();
145+
}
146+
143147
$this->persisted = true;
144148

145149
return $this;

tests/Fixtures/Entity/Post.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,21 @@ public function getTitle(): ?string
8989
return $this->title;
9090
}
9191

92+
public function setTitle(string $title): void
93+
{
94+
$this->title = $title;
95+
}
96+
9297
public function getBody(): ?string
9398
{
9499
return $this->body;
95100
}
96101

102+
public function setBody(string $body): void
103+
{
104+
$this->body = $body;
105+
}
106+
97107
public function getShortDescription(): ?string
98108
{
99109
return $this->shortDescription;

tests/Functional/FactoryTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,4 +142,50 @@ public function can_create_embeddable(): void
142142
$this->assertNull($object1->getValue());
143143
$this->assertSame('an address', $object2->getValue());
144144
}
145+
146+
public function can_delay_flush(): void
147+
{
148+
AnonymousFactory::new(Post::class)->assert()->empty();
149+
AnonymousFactory::new(Category::class)->assert()->empty();
150+
151+
AnonymousFactory::delayFlush(function() {
152+
AnonymousFactory::new(Post::class)->create([
153+
'title' => 'title',
154+
'body' => 'body',
155+
'category' => AnonymousFactory::new(Category::class, ['name' => 'name']),
156+
]);
157+
158+
AnonymousFactory::new(Post::class)->assert()->empty();
159+
AnonymousFactory::new(Category::class)->assert()->empty();
160+
});
161+
162+
AnonymousFactory::new(Post::class)->assert()->count(1);
163+
AnonymousFactory::new(Category::class)->assert()->count(1);
164+
}
165+
166+
/**
167+
* @test
168+
*/
169+
public function auto_refresh_is_disabled_during_delay_flush(): void
170+
{
171+
AnonymousFactory::new(Post::class)->assert()->empty();
172+
AnonymousFactory::new(Category::class)->assert()->empty();
173+
174+
AnonymousFactory::delayFlush(function() {
175+
$post = AnonymousFactory::new(Post::class)->create([
176+
'title' => 'title',
177+
'body' => 'body',
178+
'category' => AnonymousFactory::new(Category::class, ['name' => 'name']),
179+
]);
180+
181+
$post->setTitle('new title');
182+
$post->setBody('new body');
183+
184+
AnonymousFactory::new(Post::class)->assert()->empty();
185+
AnonymousFactory::new(Category::class)->assert()->empty();
186+
});
187+
188+
AnonymousFactory::new(Post::class)->assert()->count(1);
189+
AnonymousFactory::new(Category::class)->assert()->count(1);
190+
}
145191
}

0 commit comments

Comments
 (0)