Skip to content

Commit 3a88b8e

Browse files
committed
feature: allow to create objects in dataProvider thanks to lazy proxy
1 parent 80d67f6 commit 3a88b8e

12 files changed

+265
-144
lines changed

src/Persistence/IsProxy.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ public function _repository(): ProxyRepositoryDecorator
112112
return new ProxyRepositoryDecorator(parent::class);
113113
}
114114

115+
public function _initializeLazyObject(): void
116+
{
117+
$this->initializeLazyObject();
118+
}
119+
115120
private function _autoRefresh(): void
116121
{
117122
if (!$this->_getAutoRefresh()) {

src/Persistence/PersistentObjectFactory.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ final public static function truncate(): void
203203
static::repository()->truncate();
204204
}
205205

206-
final public function create(callable|array $attributes = []): object
206+
public function create(callable|array $attributes = []): object
207207
{
208208
$object = parent::create($attributes);
209209

src/Persistence/PersistentProxyObjectFactory.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Doctrine\Persistence\ObjectRepository;
1515
use Zenstruck\Foundry\Configuration;
16+
use Zenstruck\Foundry\Exception\FoundryNotBooted;
1617
use Zenstruck\Foundry\Factory;
1718
use Zenstruck\Foundry\Object\Instantiator;
1819
use Zenstruck\Foundry\FactoryCollection; // keep me!
@@ -36,6 +37,18 @@
3637
*/
3738
abstract class PersistentProxyObjectFactory extends PersistentObjectFactory
3839
{
40+
/**
41+
* @return T&Proxy<T>
42+
*/
43+
public function create(callable|array $attributes = []): object
44+
{
45+
try {
46+
return parent::create($attributes);
47+
} catch (FoundryNotBooted) {
48+
return ProxyGenerator::wrapFactory($this, $attributes);
49+
}
50+
}
51+
3952
/**
4053
* @return class-string<T>
4154
*/

src/Persistence/Proxy.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,9 @@ public function _real(): object;
4949
* @return ProxyRepositoryDecorator<T,ObjectRepository<T>>
5050
*/
5151
public function _repository(): ProxyRepositoryDecorator;
52+
53+
/**
54+
* @internal
55+
*/
56+
public function _initializeLazyObject(): void;
5257
}

src/Persistence/ProxyGenerator.php

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,14 @@
1515
use Symfony\Component\VarExporter\LazyObjectInterface;
1616
use Symfony\Component\VarExporter\LazyProxyTrait;
1717
use Symfony\Component\VarExporter\ProxyHelper;
18+
use Zenstruck\Foundry\Factory;
1819

1920
/**
2021
* @author Kevin Bond <[email protected]>
2122
*
2223
* @internal
24+
*
25+
* @phpstan-import-type Attributes from Factory
2326
*/
2427
final class ProxyGenerator
2528
{
@@ -43,6 +46,19 @@ public static function wrap(object $object): Proxy
4346
return self::generateClassFor($object)::createLazyProxy(static fn() => $object); // @phpstan-ignore-line
4447
}
4548

49+
/**
50+
* @template T of object
51+
*
52+
* @param PersistentProxyObjectFactory<T> $factory
53+
* @phpstan-param Attributes $attributes
54+
*
55+
* @return T&Proxy<T>
56+
*/
57+
public static function wrapFactory(PersistentProxyObjectFactory $factory, callable|array $attributes): Proxy
58+
{
59+
return self::generateClassFor($factory)::createLazyProxy(static fn() => $factory->create($attributes)); // @phpstan-ignore-line
60+
}
61+
4662
/**
4763
* @template T
4864
*
@@ -76,8 +92,8 @@ public static function unwrap(mixed $what): mixed
7692
*/
7793
private static function generateClassFor(object $object): string
7894
{
79-
/** @var class-string $class */
80-
$class = $object instanceof DoctrineProxy ? \get_parent_class($object) : $object::class;
95+
$class = self::extractClassName($object);
96+
8197
$proxyClass = self::proxyClassNameFor($class);
8298

8399
/** @var class-string<LazyObjectInterface&Proxy<T>&T> $proxyClass */
@@ -151,4 +167,16 @@ public static function proxyClassNameFor(string $class): string
151167
{
152168
return \str_replace('\\', '', $class).'Proxy';
153169
}
170+
171+
/**
172+
* @return class-string
173+
*/
174+
private static function extractClassName(object $object): string
175+
{
176+
if ($object instanceof PersistentProxyObjectFactory) {
177+
return $object::class();
178+
}
179+
180+
return $object instanceof DoctrineProxy ? \get_parent_class($object) : $object::class; // @phpstan-ignore return.type
181+
}
154182
}

src/Test/Factories.php

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@
1414
use PHPUnit\Framework\Attributes\After;
1515
use PHPUnit\Framework\Attributes\Before;
1616
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
17+
use Symfony\Component\VarExporter\LazyObjectInterface;
18+
use Webmozart\Assert\Assert;
1719
use Zenstruck\Foundry\Configuration;
20+
use Zenstruck\Foundry\Persistence\Proxy;
1821

1922
/**
2023
* @author Kevin Bond <[email protected]>
@@ -26,7 +29,16 @@ trait Factories
2629
* @before
2730
*/
2831
#[Before]
29-
public static function _bootFoundry(): void
32+
public function _beforeHook(): void
33+
{
34+
$this->_bootFoundry();
35+
$this->_loadDataProvidedProxies();
36+
}
37+
38+
/**
39+
* @internal
40+
*/
41+
private function _bootFoundry(): void
3042
{
3143
if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore-line
3244
// unit test
@@ -46,6 +58,32 @@ public static function _bootFoundry(): void
4658
});
4759
}
4860

61+
/**
62+
* @internal
63+
*/
64+
public function _loadDataProvidedProxies(): void
65+
{
66+
if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore-line
67+
return;
68+
}
69+
70+
$providedData = method_exists($this, 'getProvidedData') ? $this->getProvidedData() : $this->providedData(); // @phpstan-ignore method.notFound
71+
72+
foreach ($providedData as $providedDatum) {
73+
if ($providedDatum instanceof Proxy) {
74+
$providedDatum->_initializeLazyObject();
75+
}
76+
77+
if (is_array($providedDatum) && count($providedDatum)) {
78+
foreach ($providedDatum as $itemFromCollection) {
79+
if ($itemFromCollection instanceof Proxy) {
80+
$itemFromCollection->_initializeLazyObject();
81+
}
82+
}
83+
}
84+
}
85+
}
86+
4987
/**
5088
* @internal
5189
* @after

tests/Integration/Mongo/GenericDocumentFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class GenericDocumentFactoryTest extends GenericFactoryTestCase
2323
{
2424
use RequiresMongo;
2525

26-
protected function factory(): GenericModelFactory
26+
protected static function factory(): GenericDocumentFactory
2727
{
2828
return GenericDocumentFactory::new();
2929
}

tests/Integration/Mongo/GenericDocumentProxyFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class GenericDocumentProxyFactoryTest extends GenericProxyFactoryTestCase
2626
{
2727
use RequiresMongo;
2828

29-
protected function factory(): PersistentProxyObjectFactory
29+
protected static function factory(): GenericProxyDocumentFactory
3030
{
3131
return GenericProxyDocumentFactory::new();
3232
}

tests/Integration/ORM/GenericEntityFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class GenericEntityFactoryTest extends GenericFactoryTestCase
2323
{
2424
use RequiresORM;
2525

26-
protected function factory(): GenericModelFactory
26+
protected static function factory(): GenericEntityFactory
2727
{
2828
return GenericEntityFactory::new();
2929
}

tests/Integration/ORM/GenericEntityProxyFactoryTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class GenericEntityProxyFactoryTest extends GenericProxyFactoryTestCase
2626
{
2727
use RequiresORM;
2828

29-
protected function factory(): PersistentProxyObjectFactory
29+
protected static function factory(): GenericProxyEntityFactory
3030
{
3131
return GenericProxyEntityFactory::new();
3232
}

0 commit comments

Comments
 (0)