Skip to content

Commit

Permalink
feature: allow to create objects in dataProvider thanks to lazy proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
nikophil committed Jun 21, 2024
1 parent 80d67f6 commit 28939c2
Show file tree
Hide file tree
Showing 12 changed files with 246 additions and 144 deletions.
5 changes: 5 additions & 0 deletions src/Persistence/IsProxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public function _repository(): ProxyRepositoryDecorator
return new ProxyRepositoryDecorator(parent::class);
}

public function _initializeLazyObject(): void
{
$this->initializeLazyObject();
}

private function _autoRefresh(): void
{
if (!$this->_getAutoRefresh()) {
Expand Down
2 changes: 1 addition & 1 deletion src/Persistence/PersistentObjectFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ final public static function truncate(): void
static::repository()->truncate();
}

final public function create(callable|array $attributes = []): object
public function create(callable|array $attributes = []): object
{
$object = parent::create($attributes);

Expand Down
10 changes: 10 additions & 0 deletions src/Persistence/PersistentProxyObjectFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

use Doctrine\Persistence\ObjectRepository;
use Zenstruck\Foundry\Configuration;
use Zenstruck\Foundry\Exception\FoundryNotBooted;
use Zenstruck\Foundry\Factory;
use Zenstruck\Foundry\Object\Instantiator;
use Zenstruck\Foundry\FactoryCollection; // keep me!
Expand All @@ -36,6 +37,15 @@
*/
abstract class PersistentProxyObjectFactory extends PersistentObjectFactory
{
public function create(callable|array $attributes = []): object
{
try {
return parent::create($attributes);
} catch (FoundryNotBooted) {
return ProxyGenerator::wrapFactory($this, $attributes);
}
}

/**
* @return class-string<T>
*/
Expand Down
5 changes: 5 additions & 0 deletions src/Persistence/Proxy.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,9 @@ public function _real(): object;
* @return ProxyRepositoryDecorator<T,ObjectRepository<T>>
*/
public function _repository(): ProxyRepositoryDecorator;

/**
* @internal
*/
public function _initializeLazyObject(): void;
}
21 changes: 19 additions & 2 deletions src/Persistence/ProxyGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ public static function wrap(object $object): Proxy
return self::generateClassFor($object)::createLazyProxy(static fn() => $object); // @phpstan-ignore-line
}

public static function wrapFactory(PersistentProxyObjectFactory $factory, callable|array $attributes): Proxy

Check failure on line 46 in src/Persistence/ProxyGenerator.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method Zenstruck\Foundry\Persistence\ProxyGenerator::wrapFactory() has parameter $attributes with no value type specified in iterable type array.

Check failure on line 46 in src/Persistence/ProxyGenerator.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method Zenstruck\Foundry\Persistence\ProxyGenerator::wrapFactory() has parameter $factory with generic class Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory but does not specify its types: T

Check failure on line 46 in src/Persistence/ProxyGenerator.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method Zenstruck\Foundry\Persistence\ProxyGenerator::wrapFactory() return type with generic interface Zenstruck\Foundry\Persistence\Proxy does not specify its types: T
{
return self::generateClassFor($factory)::createLazyProxy(static fn() => $factory->create($attributes)); // @phpstan-ignore-line
}

/**
* @template T
*
Expand Down Expand Up @@ -76,8 +81,8 @@ public static function unwrap(mixed $what): mixed
*/
private static function generateClassFor(object $object): string
{
/** @var class-string $class */
$class = $object instanceof DoctrineProxy ? \get_parent_class($object) : $object::class;
$class = self::extractClassName($object);

$proxyClass = self::proxyClassNameFor($class);

/** @var class-string<LazyObjectInterface&Proxy<T>&T> $proxyClass */
Expand Down Expand Up @@ -151,4 +156,16 @@ public static function proxyClassNameFor(string $class): string
{
return \str_replace('\\', '', $class).'Proxy';
}

/**
* @return class-string
*/
private static function extractClassName(object $object): string
{
if ($object instanceof PersistentProxyObjectFactory) {
return $object::class();
}

return $object instanceof DoctrineProxy ? \get_parent_class($object) : $object::class;

Check failure on line 169 in src/Persistence/ProxyGenerator.php

View workflow job for this annotation

GitHub Actions / Static Analysis

Method Zenstruck\Foundry\Persistence\ProxyGenerator::extractClassName() should return class-string but returns class-string|false.
}
}
33 changes: 32 additions & 1 deletion src/Test/Factories.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@
use PHPUnit\Framework\Attributes\After;
use PHPUnit\Framework\Attributes\Before;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\VarExporter\LazyObjectInterface;
use Webmozart\Assert\Assert;
use Zenstruck\Foundry\Configuration;
use Zenstruck\Foundry\Persistence\Proxy;

/**
* @author Kevin Bond <[email protected]>
Expand All @@ -26,7 +29,7 @@ trait Factories
* @before
*/
#[Before]
public static function _bootFoundry(): void
public function _bootFoundry(): void
{
if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore-line
// unit test
Expand All @@ -46,6 +49,34 @@ public static function _bootFoundry(): void
});
}

/**
* @internal
* @before
*/
#[Before]
public function _loadDataProvidedProxies(): void
{
if (!\is_subclass_of(static::class, KernelTestCase::class)) { // @phpstan-ignore-line
return;
}

$providedData = method_exists($this, 'getProvidedData') ? $this->getProvidedData() : $this->providedData(); // @phpstan-ignore method.notFound

foreach ($providedData as $providedDatum) {
if ($providedDatum instanceof Proxy) {
$providedDatum->_initializeLazyObject();
}

if (is_array($providedDatum) && count($providedDatum)) {
foreach ($providedDatum as $itemFromCollection) {
if ($itemFromCollection instanceof Proxy) {
$itemFromCollection->_initializeLazyObject();
}
}
}
}
}

/**
* @internal
* @after
Expand Down
2 changes: 1 addition & 1 deletion tests/Integration/Mongo/GenericDocumentFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class GenericDocumentFactoryTest extends GenericFactoryTestCase
{
use RequiresMongo;

protected function factory(): GenericModelFactory
protected static function factory(): GenericDocumentFactory
{
return GenericDocumentFactory::new();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ final class GenericDocumentProxyFactoryTest extends GenericProxyFactoryTestCase
{
use RequiresMongo;

protected function factory(): PersistentProxyObjectFactory
protected static function factory(): GenericProxyDocumentFactory
{
return GenericProxyDocumentFactory::new();
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Integration/ORM/GenericEntityFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class GenericEntityFactoryTest extends GenericFactoryTestCase
{
use RequiresORM;

protected function factory(): GenericModelFactory
protected static function factory(): GenericEntityFactory
{
return GenericEntityFactory::new();
}
Expand Down
4 changes: 3 additions & 1 deletion tests/Integration/ORM/GenericEntityProxyFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@

use Zenstruck\Foundry\Persistence\PersistentProxyObjectFactory;
use Zenstruck\Foundry\Tests\Fixture\Entity\EdgeCases\EntityWithReadonly\EntityWithReadonly;
use Zenstruck\Foundry\Tests\Fixture\Entity\GenericEntity;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\GenericEntityFactory;
use Zenstruck\Foundry\Tests\Fixture\Factories\Entity\GenericProxyEntityFactory;
use Zenstruck\Foundry\Tests\Integration\Persistence\GenericProxyFactoryTestCase;
use Zenstruck\Foundry\Tests\Integration\RequiresORM;
Expand All @@ -26,7 +28,7 @@ final class GenericEntityProxyFactoryTest extends GenericProxyFactoryTestCase
{
use RequiresORM;

protected function factory(): PersistentProxyObjectFactory
protected static function factory(): GenericProxyEntityFactory
{
return GenericProxyEntityFactory::new();
}
Expand Down
Loading

0 comments on commit 28939c2

Please sign in to comment.