Skip to content

Commit 3a46f03

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

15 files changed

+273
-147
lines changed

bin/console

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
<?php
33

44
use Symfony\Bundle\FrameworkBundle\Console\Application;
5-
use Zenstruck\Foundry\Tests\Fixtures\Kernel;
5+
use Zenstruck\Foundry\Tests\Fixture\TestKernel;
66

77
require_once __DIR__ . '/../tests/bootstrap.php';
88

9-
$application = new Application(new Kernel('test', true));
9+
$application = new Application(new TestKernel('test', true));
1010
$application->run();

src/Persistence/IsProxy.php

+5
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

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

206-
final public function create(callable|array $attributes = []): object
206+
/**
207+
* @final
208+
*/
209+
public function create(callable|array $attributes = []): object
207210
{
208211
$object = parent::create($attributes);
209212

src/Persistence/PersistentProxyObjectFactory.php

+13
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+
final 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

+5
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

+30-2
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/Persistence/functions.php

+12
Original file line numberDiff line numberDiff line change
@@ -171,3 +171,15 @@ function enable_persisting(): void
171171
{
172172
Configuration::instance()->persistence()->enablePersisting();
173173
}
174+
175+
/**
176+
* @internal
177+
*/
178+
function initialize_proxy_object(mixed $what): void
179+
{
180+
match(true) {
181+
$what instanceof Proxy => $what->_initializeLazyObject(),
182+
is_array($what) => array_map(initialize_proxy_object(...), $what),
183+
default => true // do nothing
184+
};
185+
}

src/Test/Factories.php

+28-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@
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;
21+
use function Zenstruck\Foundry\Persistence\initialize_proxy_object;
1822

1923
/**
2024
* @author Kevin Bond <[email protected]>
@@ -26,7 +30,16 @@ trait Factories
2630
* @before
2731
*/
2832
#[Before]
29-
public static function _bootFoundry(): void
33+
public function _beforeHook(): void
34+
{
35+
$this->_bootFoundry();
36+
$this->_loadDataProvidedProxies();
37+
}
38+
39+
/**
40+
* @internal
41+
*/
42+
private function _bootFoundry(): void
3043
{
3144
if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore-line
3245
// unit test
@@ -46,6 +59,20 @@ public static function _bootFoundry(): void
4659
});
4760
}
4861

62+
/**
63+
* @internal
64+
*/
65+
private function _loadDataProvidedProxies(): void
66+
{
67+
if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore-line
68+
return;
69+
}
70+
71+
$providedData = method_exists($this, 'getProvidedData') ? $this->getProvidedData() : $this->providedData(); // @phpstan-ignore method.notFound
72+
73+
initialize_proxy_object($providedData);
74+
}
75+
4976
/**
5077
* @internal
5178
* @after

tests/Integration/Mongo/GenericDocumentFactoryTest.php

+1-1
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

+1-1
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

+1-1
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

+1-1
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)