Skip to content

Commit c8b541d

Browse files
committed
feat: allow to use Factory::create() in data provider
1 parent 41f15ca commit c8b541d

33 files changed

+913
-181
lines changed

.env

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ DATABASE_URL="mysql://root:[email protected]:3307/foundry_test?serverVersion=5.7.42
22
MONGO_URL="mongodb://127.0.0.1:27018/dbName?compressors=disabled&gssapiServiceName=mongodb"
33
DATABASE_RESET_MODE="schema"
44
USE_DAMA_DOCTRINE_TEST_BUNDLE="0"
5+
USE_FOUNDRY_PHPUNIT_EXTENSION="0"
56
PHPUNIT_VERSION="9"

.github/workflows/ci.yml

+23-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ on:
88

99
jobs:
1010
tests:
11-
name: P:${{ matrix.php }}, S:${{ matrix.symfony }}, D:${{ matrix.database }}, PU:${{ matrix.phpunit }}${{ matrix.deps == 'lowest' && ' (lowest)' || '' }}${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && ' (dama)' || '' }}${{ !contains(matrix.database, 'sql') && '' || matrix.use-migrate == 1 && ' (migrate)' || ' (schema)' }}
11+
name: P:${{ matrix.php }}, S:${{ matrix.symfony }}, D:${{ matrix.database }}, PU:${{ matrix.phpunit }}${{ matrix.deps == 'lowest' && ' (lowest)' || '' }}${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && ' (dama)' || '' }}${{ !contains(matrix.database, 'sql') && '' || matrix.use-migrate == 1 && ' (migrate)' || ' (schema)' }}${{ matrix.use-phpunit-extension == 1 && ' (phpunit extension)' || '' }}
1212
runs-on: ubuntu-latest
1313
strategy:
1414
fail-fast: false
@@ -19,6 +19,7 @@ jobs:
1919
database: [ mysql, mongo ]
2020
use-dama: [ 1 ]
2121
use-migrate: [ 0 ]
22+
use-phpunit-extension: [ 0 ]
2223
phpunit: [ 9 ]
2324
exclude:
2425
- php: 8.1
@@ -32,74 +33,93 @@ jobs:
3233
database: none
3334
use-dama: 1
3435
use-migrate: 0
36+
use-phpunit-extension: 0
3537
phpunit: 9
3638
- php: 8.3
3739
deps: highest
3840
symfony: '*'
3941
database: mysql|mongo
4042
use-dama: 1
4143
use-migrate: 0
44+
use-phpunit-extension: 0
4245
phpunit: 9
4346
- php: 8.3
4447
deps: highest
4548
symfony: '*'
4649
database: pgsql|mongo
4750
use-dama: 1
4851
use-migrate: 0
52+
use-phpunit-extension: 0
4953
phpunit: 9
5054
- php: 8.3
5155
deps: highest
5256
symfony: '*'
5357
database: pgsql
5458
use-dama: 0
5559
use-migrate: 0
60+
use-phpunit-extension: 0
5661
phpunit: 9
5762
- php: 8.3
5863
deps: highest
5964
symfony: '*'
6065
database: sqlite
6166
use-dama: 0
6267
use-migrate: 0
68+
use-phpunit-extension: 0
6369
phpunit: 9
6470
- php: 8.3
6571
deps: lowest
6672
symfony: '*'
6773
database: sqlite
6874
use-dama: 0
6975
use-migrate: 0
76+
use-phpunit-extension: 0
7077
phpunit: 9
7178
- php: 8.3
7279
deps: lowest
7380
symfony: '*'
7481
database: mysql
7582
use-dama: 1
7683
use-migrate: 0
84+
use-phpunit-extension: 0
7785
phpunit: 9
7886
- php: 8.3
7987
deps: highest
8088
symfony: '*'
8189
database: mysql
8290
use-dama: 1
8391
use-migrate: 1
92+
use-phpunit-extension: 0
8493
phpunit: 9
8594
- php: 8.3
8695
deps: highest
8796
symfony: '*'
8897
database: mysql|mongo
8998
use-dama: 1
9099
use-migrate: 0
100+
use-phpunit-extension: 0
91101
phpunit: 10
92102
- php: 8.3
93103
deps: highest
94104
symfony: '*'
95105
database: mysql|mongo
96106
use-dama: 1
97107
use-migrate: 0
108+
use-phpunit-extension: 0
98109
phpunit: 11
110+
- php: 8.3
111+
deps: highest
112+
symfony: '*'
113+
database: mysql|mongo
114+
use-dama: 1
115+
use-migrate: 0
116+
use-phpunit-extension: 1
117+
phpunit: 11.4
99118
env:
100119
DATABASE_URL: ${{ contains(matrix.database, 'mysql') && 'mysql://root:root@localhost:3306/foundry?serverVersion=5.7.42' || contains(matrix.database, 'pgsql') && 'postgresql://root:root@localhost:5432/foundry?serverVersion=15' || contains(matrix.database, 'sqlite') && 'sqlite:///%kernel.project_dir%/var/data.db' || '' }}
101120
MONGO_URL: ${{ contains(matrix.database, 'mongo') && 'mongodb://127.0.0.1:27017/dbName?compressors=disabled&gssapiServiceName=mongodb' || '' }}
102121
USE_DAMA_DOCTRINE_TEST_BUNDLE: ${{ matrix.use-dama == 1 && contains(matrix.database, 'sql') && 1 || 0 }}
122+
USE_FOUNDRY_PHPUNIT_EXTENSION: ${{ matrix.use-phpunit-extension }}
103123
PHPUNIT_VERSION: ${{ matrix.phpunit }}
104124
services:
105125
postgres:
@@ -155,7 +175,8 @@ jobs:
155175
DATABASE_URL: postgresql://root:root@localhost:5432/foundry?serverVersion=15
156176
MONGO_URL: mongodb://127.0.0.1:27017/dbName?compressors=disabled&gssapiServiceName=mongodb
157177
USE_DAMA_DOCTRINE_TEST_BUNDLE: 1
158-
PHPUNIT_VERSION: 9
178+
USE_FOUNDRY_PHPUNIT_EXTENSION: 1
179+
PHPUNIT_VERSION: 11.4
159180
services:
160181
mongo:
161182
image: mongo:4

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();

phpstan.neon

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ parameters:
1414
- identifier: missingType.iterableValue
1515
path: tests/
1616

17+
# We support both PHPUnit versions (this method changed in PHPUnit 10)
18+
- message: '#Call to function method_exists\(\) with .* will always evaluate to false#'
19+
path: src/Test/Factories.php
20+
1721
excludePaths:
1822
- tests/Fixture/Maker/expected/can_create_factory_with_auto_activated_not_persisted_option.php
1923
- tests/Fixture/Maker/expected/can_create_factory_interactively.php

phpunit

+18-13
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,21 @@ if [ "${SHOULD_UPDATE_PHPUNIT}" = "0" ]; then
4949
fi
5050
### <<
5151

52-
### >> guess extensions
53-
EXTENSION=""
52+
### >> actually execute PHPUnit with the right options
53+
DAMA_EXTENSION="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"
54+
FOUNDRY_EXTENSION="Zenstruck\Foundry\Test\PHPUnit\Extension"
5455

55-
if [ "${USE_DAMA_DOCTRINE_TEST_BUNDLE:-0}" = "1" ]; then
56-
EXTENSION="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"
56+
if [ "${USE_FOUNDRY_PHPUNIT_EXTENSION:-0}" = "1" ] && [ "${PHPUNIT_VERSION}" != "11.4" ]; then
57+
echo "❌ USE_FOUNDRY_PHPUNIT_EXTENSION could only be used with PHPUNIT_VERSION=11.4";
58+
exit 1;
5759
fi
58-
### <<
5960

60-
### >> actually execute PHPUnit with the right options
6161
case ${PHPUNIT_VERSION} in
6262
"9")
63-
if [ -z "${EXTENSION}" ]; then
64-
vendor/bin/phpunit -c phpunit.xml.dist "$@"
63+
if [ "${USE_DAMA_DOCTRINE_TEST_BUNDLE:-0}" = "1" ]; then
64+
vendor/bin/phpunit -c phpunit.xml.dist --extensions "${DAMA_EXTENSION}" "$@"
6565
else
66-
vendor/bin/phpunit -c phpunit.xml.dist --extensions "${EXTENSION}" "$@"
66+
vendor/bin/phpunit -c phpunit.xml.dist "$@"
6767
fi
6868
;;
6969

@@ -73,11 +73,16 @@ case ${PHPUNIT_VERSION} in
7373
;;
7474

7575
"11"|"11.4")
76-
if [ -z "${EXTENSION}" ]; then
77-
vendor/bin/phpunit -c phpunit-10.xml.dist "$@"
78-
else
79-
vendor/bin/phpunit -c phpunit-10.xml.dist --extension "${EXTENSION}" "$@"
76+
PHPUNIT_EXEC="vendor/bin/phpunit -c phpunit-10.xml.dist $@"
77+
if [ "${USE_DAMA_DOCTRINE_TEST_BUNDLE:-0}" = "1" ]; then
78+
PHPUNIT_EXEC="${PHPUNIT_EXEC} --extension "${DAMA_EXTENSION}""
8079
fi
80+
81+
if [ "${USE_FOUNDRY_PHPUNIT_EXTENSION:-0}" = "1" ]; then
82+
PHPUNIT_EXEC="${PHPUNIT_EXEC} --extension "${FOUNDRY_EXTENSION}""
83+
fi
84+
85+
$PHPUNIT_EXEC
8186
;;
8287
esac
8388
### <<

phpunit-10.xml.dist

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="UTF-8"?>
22
<!-- https://phpunit.de/manual/current/en/appendixes.configuration.html -->
33
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4-
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
4+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
55
bootstrap="tests/bootstrap.php"
66
colors="true"
77
failOnRisky="true"

src/Configuration.php

+21-5
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,12 @@ final class Configuration
3333
*/
3434
public $instantiator;
3535

36-
/** @var \Closure():self|self|null */
37-
private static \Closure|self|null $instance = null;
36+
/**
37+
* This property is only filled if the PHPUnit extension is used!
38+
*/
39+
private bool $bootedForDataProvider = false;
40+
41+
private static ?self $instance = null;
3842

3943
/**
4044
* @param InstantiatorCallable $instantiator
@@ -66,13 +70,18 @@ public function assertPersistanceEnabled(): void
6670
}
6771
}
6872

73+
public function inADataProvider(): bool
74+
{
75+
return $this->bootedForDataProvider;
76+
}
77+
6978
public static function instance(): self
7079
{
7180
if (!self::$instance) {
72-
throw new FoundryNotBooted('Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.');
81+
throw new FoundryNotBooted();
7382
}
7483

75-
return \is_callable(self::$instance) ? (self::$instance)() : self::$instance;
84+
return self::$instance;
7685
}
7786

7887
public static function isBooted(): bool
@@ -82,7 +91,14 @@ public static function isBooted(): bool
8291

8392
public static function boot(\Closure|self $configuration): void
8493
{
85-
self::$instance = $configuration;
94+
self::$instance = \is_callable($configuration) ? ($configuration)() : $configuration;
95+
self::$instance->bootedForDataProvider = false;
96+
}
97+
98+
public static function bootForDataProvider(\Closure|self $configuration): void
99+
{
100+
self::$instance = \is_callable($configuration) ? ($configuration)() : $configuration;
101+
self::$instance->bootedForDataProvider = true;
86102
}
87103

88104
public static function shutdown(): void

src/Exception/FoundryNotBooted.php

+4
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@
1616
*/
1717
final class FoundryNotBooted extends \LogicException
1818
{
19+
public function __construct()
20+
{
21+
parent::__construct('Foundry is not yet booted. Ensure ZenstruckFoundryBundle is enabled. If in a test, ensure your TestCase has the Factories trait.');
22+
}
1923
}

src/Persistence/IsProxy.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Zenstruck\Foundry\Persistence;
1313

14-
use Doctrine\ODM\MongoDB\DocumentManager;
1514
use Symfony\Component\VarExporter\LazyProxyTrait;
1615
use Zenstruck\Assert;
1716
use Zenstruck\Foundry\Configuration;
@@ -128,6 +127,11 @@ public function _assertNotPersisted(string $message = '{entity} is persisted but
128127
return $this;
129128
}
130129

130+
public function _initializeLazyObject(): void
131+
{
132+
$this->initializeLazyObject();
133+
}
134+
131135
private function isPersisted(): bool
132136
{
133137
try {

src/Persistence/PersistenceStrategy.php

-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
namespace Zenstruck\Foundry\Persistence;
1313

14-
use Doctrine\ORM\Mapping\ClassMetadataInfo;
1514
use Doctrine\Persistence\ManagerRegistry;
1615
use Doctrine\Persistence\Mapping\ClassMetadata;
1716
use Doctrine\Persistence\Mapping\MappingException;

src/Persistence/PersistentObjectFactory.php

+26-1
Original file line numberDiff line numberDiff line change
@@ -204,12 +204,15 @@ final public static function truncate(): void
204204
}
205205

206206
/**
207+
* @final
207208
* @return T
208209
*/
209-
final public function create(callable|array $attributes = []): object
210+
public function create(callable|array $attributes = []): object
210211
{
211212
$object = parent::create($attributes);
212213

214+
$this->throwIfCannotCreateObject();
215+
213216
if (!$this->isPersisting()) {
214217
return $this->proxy($object);
215218
}
@@ -361,4 +364,26 @@ private function proxy(object $object): object
361364

362365
return $this->isPersisting() ? $object : $object->_disableAutoRefresh();
363366
}
367+
368+
private function throwIfCannotCreateObject(): void
369+
{
370+
$configuration = Configuration::instance();
371+
372+
/**
373+
* "false === $configuration->inADataProvider()" would also mean that the PHPUnit extension is NOT used
374+
* so a `FoundryNotBooted` exception would be thrown if we actually are in a data provider.
375+
*/
376+
if (!$configuration->inADataProvider()) {
377+
return;
378+
}
379+
380+
if (
381+
!$configuration->isPersistenceAvailable()
382+
|| $this instanceof PersistentProxyObjectFactory
383+
) {
384+
return;
385+
}
386+
387+
throw new \LogicException(\sprintf('Cannot create object in a data provider for non-proxy factories. Transform your factory into a "%s", or call "create()" method in the test. See https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#phpunit-data-providers', PersistentProxyObjectFactory::class));
388+
}
364389
}

src/Persistence/PersistentProxyObjectFactory.php

+14-1
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@
1414
use Doctrine\Persistence\ObjectRepository;
1515
use Zenstruck\Foundry\Configuration;
1616
use Zenstruck\Foundry\Factory;
17-
use Zenstruck\Foundry\Object\Instantiator;
1817
use Zenstruck\Foundry\FactoryCollection; // keep me!
18+
use Zenstruck\Foundry\Object\Instantiator;
1919

2020
/**
2121
* @author Kevin Bond <[email protected]>
@@ -43,6 +43,19 @@
4343
*/
4444
abstract class PersistentProxyObjectFactory extends PersistentObjectFactory
4545
{
46+
/**
47+
* @return T&Proxy<T>
48+
*/
49+
final public function create(callable|array $attributes = []): object
50+
{
51+
$configuration = Configuration::instance();
52+
if ($configuration->inADataProvider()) {
53+
return ProxyGenerator::wrapFactory($this, $attributes);
54+
}
55+
56+
return parent::create($attributes);
57+
}
58+
4659
/**
4760
* @return class-string<T>
4861
*/

src/Persistence/Proxy.php

+5
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,9 @@ public function _assertNotPersisted(string $message = '{entity} is persisted but
5353
* @return ProxyRepositoryDecorator<T,ObjectRepository<T>>
5454
*/
5555
public function _repository(): ProxyRepositoryDecorator;
56+
57+
/**
58+
* @internal
59+
*/
60+
public function _initializeLazyObject(): void;
5661
}

0 commit comments

Comments
 (0)