diff --git a/composer.json b/composer.json index fdb5632..16839e5 100644 --- a/composer.json +++ b/composer.json @@ -114,6 +114,7 @@ "symfony/debug-bundle": "8.1.*", "symfony/maker-bundle": "^1.67", "symfony/stopwatch": "8.1.*", - "symfony/web-profiler-bundle": "8.1.*" + "symfony/web-profiler-bundle": "8.1.*", + "zenstruck/foundry": "^2.10" } } diff --git a/composer.lock b/composer.lock index bf4eb2b..d6b1918 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cd40b73a97f584ed609ab7a2dca91ce9", + "content-hash": "58ab8f1d3e73706e3387d4c6f3f711a9", "packages": [ { "name": "composer/semver", @@ -7772,6 +7772,69 @@ }, "time": "2026-01-21T07:39:44+00:00" }, + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.13.4", @@ -10354,6 +10417,193 @@ } ], "time": "2025-12-08T11:19:18+00:00" + }, + { + "name": "zenstruck/assert", + "version": "v1.7.0", + "source": { + "type": "git", + "url": "https://github.com/zenstruck/assert.git", + "reference": "1e32d48847d4e82c345112ca226b21a1a792af0a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zenstruck/assert/zipball/1e32d48847d4e82c345112ca226b21a1a792af0a", + "reference": "1e32d48847d4e82c345112ca226b21a1a792af0a", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-php81": "^1.23", + "symfony/var-exporter": "^5.4|^6.0|^7.0|^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5.21", + "symfony/phpunit-bridge": "^6.3|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Zenstruck\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kevin Bond", + "email": "kevinbond@gmail.com" + } + ], + "description": "Standalone, lightweight, framework agnostic, test assertion library.", + "homepage": "https://github.com/zenstruck/assert", + "keywords": [ + "assertion", + "phpunit", + "test" + ], + "support": { + "issues": "https://github.com/zenstruck/assert/issues", + "source": "https://github.com/zenstruck/assert/tree/v1.7.0" + }, + "funding": [ + { + "url": "https://github.com/kbond", + "type": "github" + }, + { + "url": "https://github.com/nikophil", + "type": "github" + } + ], + "time": "2025-12-07T01:59:12+00:00" + }, + { + "name": "zenstruck/foundry", + "version": "v2.10.1", + "source": { + "type": "git", + "url": "https://github.com/zenstruck/foundry.git", + "reference": "34cbd882df0fee5a5f9bd2f0c2f69286e283263d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zenstruck/foundry/zipball/34cbd882df0fee5a5f9bd2f0c2f69286e283263d", + "reference": "34cbd882df0fee5a5f9bd2f0c2f69286e283263d", + "shasum": "" + }, + "require": { + "fakerphp/faker": "^1.24", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.2|^3.0", + "symfony/polyfill-php84": "^1.32", + "symfony/polyfill-php85": "^1.33", + "symfony/property-access": "^6.4|^7.0|^8.0", + "symfony/property-info": "^6.4|^7.0|^8.0", + "symfony/var-exporter": "^6.4.9|~7.0.9|^7.1.2|^8.0", + "zenstruck/assert": "^1.4" + }, + "conflict": { + "doctrine/cache": "<1.12.1", + "doctrine/persistence": "<2.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8", + "dama/doctrine-test-bundle": "^8.0", + "doctrine/collections": "^1.7|^2.0", + "doctrine/common": "^3.2.2", + "doctrine/doctrine-bundle": "^2.10|^3.0", + "doctrine/doctrine-migrations-bundle": "^2.2|^3.0", + "doctrine/mongodb-odm": "^2.4", + "doctrine/mongodb-odm-bundle": "^4.6|^5.0", + "doctrine/orm": "^2.16|^3.0", + "doctrine/persistence": "^2.0|^3.0|^4.0", + "phpunit/phpunit": "^9.5.0 || ^10.0 || ^11.0 || ^12.0 || ^13.0", + "symfony/browser-kit": "^6.4|^7.0|^8.0", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/dotenv": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/flex": "^2.10", + "symfony/framework-bundle": "^6.4|^7.0|^8.0", + "symfony/maker-bundle": "^1.55", + "symfony/phpunit-bridge": "^6.4.26|^7.0|^8.0", + "symfony/routing": "^6.4|^7.0|^8.0", + "symfony/runtime": "^6.4|^7.0|^8.0", + "symfony/translation-contracts": "^3.4", + "symfony/uid": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0", + "symfony/yaml": "^6.4|^7.0|^8.0", + "webmozart/assert": "^1.11" + }, + "type": "library", + "extra": { + "psalm": { + "pluginClass": "Zenstruck\\Foundry\\Psalm\\FoundryPlugin" + }, + "symfony": { + "allow-contrib": false + }, + "bamarni-bin": { + "bin-links": true, + "forward-command": false, + "target-directory": "bin/tools" + } + }, + "autoload": { + "files": [ + "src/functions.php", + "src/Persistence/functions.php", + "src/symfony_console.php" + ], + "psr-4": { + "Zenstruck\\Foundry\\": "src/", + "Zenstruck\\Foundry\\Psalm\\": "utils/psalm", + "Zenstruck\\Foundry\\Utils\\Rector\\": "utils/rector/src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kevin Bond", + "email": "kevinbond@gmail.com" + }, + { + "name": "Nicolas PHILIPPE", + "email": "nikophil@gmail.com" + } + ], + "description": "A model factory library for creating expressive, auto-completable, on-demand dev/test fixtures with Symfony and Doctrine.", + "homepage": "https://github.com/zenstruck/foundry", + "keywords": [ + "Fixture", + "dev", + "doctrine", + "factory", + "faker", + "symfony", + "test" + ], + "support": { + "issues": "https://github.com/zenstruck/foundry/issues", + "source": "https://github.com/zenstruck/foundry/tree/v2.10.1" + }, + "funding": [ + { + "url": "https://github.com/kbond", + "type": "github" + }, + { + "url": "https://github.com/nikophil", + "type": "github" + } + ], + "time": "2026-05-19T15:59:32+00:00" } ], "aliases": [], diff --git a/config/bundles.php b/config/bundles.php index be20bb7..a314b4e 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -14,4 +14,5 @@ return [ Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], DAMA\DoctrineTestBundle\DAMADoctrineTestBundle::class => ['test' => true], + Zenstruck\Foundry\ZenstruckFoundryBundle::class => ['dev' => true, 'test' => true], ]; diff --git a/config/packages/zenstruck_foundry.yaml b/config/packages/zenstruck_foundry.yaml new file mode 100644 index 0000000..2f60dd0 --- /dev/null +++ b/config/packages/zenstruck_foundry.yaml @@ -0,0 +1,16 @@ +when@dev: &dev + # See full configuration: https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#full-default-bundle-configuration + zenstruck_foundry: + persistence: + # Flush only once per call of `PersistentObjectFactory::create()` + flush_once: true + + # If you use the `make:factory --test` command, you may need to uncomment the following. + # See https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#generate + #services: + # App\Tests\Factory\: + # resource: '%kernel.project_dir%/tests/Factory/' + # autowire: true + # autoconfigure: true + +when@test: *dev diff --git a/config/reference.php b/config/reference.php index 82c752f..f96f96e 100644 --- a/config/reference.php +++ b/config/reference.php @@ -1480,6 +1480,50 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * enable_static_query_cache?: bool|Param, // Default: true * connection_keys?: list, * } + * @psalm-type ZenstruckFoundryConfig = array{ + * auto_refresh_proxies?: bool|Param|null, // Deprecated: Since 2.0 auto_refresh_proxies defaults to true and this configuration has no effect. // Whether to auto-refresh proxies by default (https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#auto-refresh) // Default: null + * enable_auto_refresh_with_lazy_objects?: bool|Param|null, // Enable auto-refresh using PHP 8.4 lazy objects (cannot be enabled if PHP < 8.4). // Default: null + * faker?: array{ // Configure the faker used by your factories. + * locale?: scalar|Param|null, // The default locale to use for faker. // Default: null + * seed?: scalar|Param|null, // Deprecated: The "faker.seed" configuration is deprecated and will be removed in 3.0. Use environment variable "FOUNDRY_FAKER_SEED" instead. // Random number generator seed to produce the same fake values every run. // Default: null + * manage_seed?: bool|Param, // Automatically manage faker seed to ensure consistent data between test runs. // Default: true + * service?: scalar|Param|null, // Service id for custom faker instance. // Default: null + * }, + * instantiator?: array{ // Configure the default instantiator used by your object factories. + * use_constructor?: bool|Param, // Use the constructor to instantiate objects. // Default: true + * allow_extra_attributes?: bool|Param, // Whether or not to skip attributes that do not correspond to properties. // Default: false + * always_force_properties?: bool|Param, // Whether or not to skip setters and force set object properties (public/private/protected) directly. // Default: false + * service?: scalar|Param|null, // Service id of your custom instantiator. // Default: null + * }, + * global_state?: list, + * persistence?: array{ + * flush_once?: bool|Param, // Flush only once per call of `PersistentObjectFactory::create()` in userland. // Default: false + * }, + * orm?: array{ + * auto_persist?: bool|Param, // Deprecated: Since 2.4 auto_persist defaults to true and this configuration has no effect. // Automatically persist entities when created. // Default: true + * reset?: array{ + * connections?: list, + * entity_managers?: list, + * mode?: \Zenstruck\Foundry\ORM\ResetDatabase\ResetDatabaseMode::SCHEMA|\Zenstruck\Foundry\ORM\ResetDatabase\ResetDatabaseMode::MIGRATE|Param, // Reset mode to use with ResetDatabase trait // Default: "schema" + * migrations?: array{ + * configurations?: list, + * }, + * }, + * }, + * mongo?: array{ + * auto_persist?: bool|Param, // Deprecated: Since 2.4 auto_persist defaults to true and this configuration has no effect. // Automatically persist documents when created. // Default: true + * reset?: array{ + * document_managers?: list, + * }, + * }, + * make_factory?: array{ + * default_namespace?: scalar|Param|null, // Default namespace where factories will be created by maker. // Default: "Factory" + * add_hints?: bool|Param, // Add "beginner" hints in the created factory. // Default: true + * }, + * make_story?: array{ + * default_namespace?: scalar|Param|null, // Default namespace where stories will be created by maker. // Default: "Story" + * }, + * } * @psalm-type ConfigType = array{ * imports?: ImportsConfig, * parameters?: ParametersConfig, @@ -1509,6 +1553,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * security?: SecurityConfig, * monolog?: MonologConfig, * maker?: MakerConfig, + * zenstruck_foundry?: ZenstruckFoundryConfig, * }, * "when@prod"?: array{ * imports?: ImportsConfig, @@ -1539,6 +1584,7 @@ use Symfony\Component\Config\Loader\ParamConfigurator as Param; * security?: SecurityConfig, * monolog?: MonologConfig, * dama_doctrine_test?: DamaDoctrineTestConfig, + * zenstruck_foundry?: ZenstruckFoundryConfig, * }, * ... + diff --git a/src/Factory/RestaurantFactory.php b/src/Factory/RestaurantFactory.php new file mode 100644 index 0000000..6e2ca81 --- /dev/null +++ b/src/Factory/RestaurantFactory.php @@ -0,0 +1,52 @@ + + */ +final class RestaurantFactory extends PersistentObjectFactory +{ + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#factories-as-services + * + * @todo inject services if required + */ + public function __construct() {} + + #[\Override] + public static function class(): string + { + return Restaurant::class; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#model-factories + * + * @todo add your default values here + */ + #[\Override] + protected function defaults(): array|callable + { + return [ + 'name' => self::faker()->company(), + ]; + } + + /** + * @see https://symfony.com/bundles/ZenstruckFoundryBundle/current/index.html#initialization + */ + #[\Override] + protected function initialize(): static + { + return $this + // ->afterInstantiate(function(Restaurant $restaurant): void {}) + ; + } +} diff --git a/src/Story/AppStory.php b/src/Story/AppStory.php new file mode 100644 index 0000000..5cc5657 --- /dev/null +++ b/src/Story/AppStory.php @@ -0,0 +1,15 @@ +request('GET', '/'); + + $this->assertSelectorTextContains('ul li', $subject->getName()); + } + public function testAddingNewSubject(): void { $client = static::createClient(); @@ -17,11 +29,15 @@ class SubjectControllerTest extends WebTestCase $this->assertResponseRedirects(); + $crawler = $client->followRedirect(); + $subject = static::getContainer() ->get(SubjectRepository::class) ->findOneBy(['name' => 'Riviera']); $this->assertNotNull($subject); $this->assertInstanceOf(Restaurant::class, $subject); + + $this->assertSelectorTextContains('ul li', 'Riviera'); } }