diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 6d42dee..a948ea6 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -4,6 +4,11 @@ parameters: - tests/IntegrationTest.php ignoreErrors: + - + message: "#^Call to an undefined method Doctrine\\\\Bundle\\\\FixturesBundle\\\\Loader\\\\SymfonyFixturesLoader\\:\\:getFixture\\(\\)\\.$#" + count: 1 + path: src/Loader/SymfonyFixturesLoader.php + - message: "#^Call to an undefined static method Doctrine\\\\Bundle\\\\FixturesBundle\\\\Loader\\\\SymfonyBridgeLoader\\:\\:addFixture\\(\\)\\.$#" count: 1 diff --git a/src/Loader/SymfonyFixturesLoader.php b/src/Loader/SymfonyFixturesLoader.php index 82b9d81..17bb73a 100644 --- a/src/Loader/SymfonyFixturesLoader.php +++ b/src/Loader/SymfonyFixturesLoader.php @@ -10,9 +10,8 @@ use Doctrine\Common\DataFixtures\FixtureInterface; use LogicException; use ReflectionClass; -use RuntimeException; -use function array_key_exists; +use function array_keys; use function array_values; use function get_class; use function sprintf; @@ -99,19 +98,22 @@ public function getFixtures(array $groups = []): array return $fixtures; } - $filteredFixtures = []; - foreach ($fixtures as $fixture) { - foreach ($groups as $group) { - $fixtureClass = get_class($fixture); - if (isset($this->groupsFixtureMapping[$group][$fixtureClass])) { - $filteredFixtures[$fixtureClass] = $fixture; - continue 2; - } + $requiredFixtures = []; + foreach ($groups as $group) { + if (! isset($this->groupsFixtureMapping[$group])) { + continue; } + + $requiredFixtures += $this->collectDependencies(...array_keys($this->groupsFixtureMapping[$group])); } - foreach ($filteredFixtures as $fixture) { - $this->validateDependencies($filteredFixtures, $fixture); + $filteredFixtures = []; + foreach ($fixtures as $order => $fixture) { + $fixtureClass = get_class($fixture); + if (isset($requiredFixtures[$fixtureClass])) { + $filteredFixtures[$order] = $fixture; + continue; + } } return array_values($filteredFixtures); @@ -130,22 +132,25 @@ private function addGroupsFixtureMapping(string $className, array $groups): void } /** - * @param string[] $fixtures An array of fixtures with class names as keys + * Collect any dependent fixtures from the given classes. * - * @throws RuntimeException + * @psalm-return array */ - private function validateDependencies(array $fixtures, FixtureInterface $fixture): void + private function collectDependencies(string ...$fixtureClass): array { - if (! $fixture instanceof DependentFixtureInterface) { - return; - } + $dependencies = []; - $dependenciesClasses = $fixture->getDependencies(); + foreach ($fixtureClass as $class) { + $dependencies[$class] = true; + $fixture = $this->getFixture($class); - foreach ($dependenciesClasses as $class) { - if (! array_key_exists($class, $fixtures)) { - throw new RuntimeException(sprintf('Fixture "%s" was declared as a dependency for fixture "%s", but it was not included in any of the loaded fixture groups.', $class, get_class($fixture))); + if (! $fixture instanceof DependentFixtureInterface) { + continue; } + + $dependencies += $this->collectDependencies(...$fixture->getDependencies()); } + + return $dependencies; } } diff --git a/tests/Fixtures/FooBundle/DataFixtures/WithDeepDependenciesFixtures.php b/tests/Fixtures/FooBundle/DataFixtures/WithDeepDependenciesFixtures.php new file mode 100644 index 0000000..06ffbfe --- /dev/null +++ b/tests/Fixtures/FooBundle/DataFixtures/WithDeepDependenciesFixtures.php @@ -0,0 +1,34 @@ +assertCount(0, $loader->getFixtures(['group3'])); } - public function testLoadFixturesViaGroupWithMissingDependency(): void + public function testLoadFixturesViaGroupWithDependenciesNotInGroup(): void { $kernel = new IntegrationTestKernel('dev', true); $kernel->addServices(static function (ContainerBuilder $c): void { - // has a "staging" group via the getGroups() method $c->autowire(OtherFixtures::class) ->addTag(FixturesCompilerPass::FIXTURE_TAG); - // no getGroups() method $c->autowire(WithDependenciesFixtures::class) ->addTag(FixturesCompilerPass::FIXTURE_TAG); + $c->autowire(WithDeepDependenciesFixtures::class) + ->addTag(FixturesCompilerPass::FIXTURE_TAG); + + $c->autowire(RequiredConstructorArgsFixtures::class) + ->setArgument('$fooRequiredArg', 'test') + ->addTag(FixturesCompilerPass::FIXTURE_TAG); + + $c->autowire(DependentOnRequiredConstructorArgsFixtures::class) + ->addTag(FixturesCompilerPass::FIXTURE_TAG); + $c->setAlias('test.doctrine.fixtures.loader', new Alias('doctrine.fixtures.loader', true)); }); $kernel->boot(); @@ -193,10 +202,8 @@ public function testLoadFixturesViaGroupWithMissingDependency(): void $loader = $container->get('test.doctrine.fixtures.loader'); $this->assertInstanceOf(SymfonyFixturesLoader::class, $loader); - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Fixture "Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\OtherFixtures" was declared as a dependency for fixture "Doctrine\Bundle\FixturesBundle\Tests\Fixtures\FooBundle\DataFixtures\WithDependenciesFixtures", but it was not included in any of the loaded fixture groups.'); - - $loader->getFixtures(['missingDependencyGroup']); + self::assertCount(2, $loader->getFixtures(['groupWithDependencies'])); + self::assertCount(5, $loader->getFixtures(['groupWithDeepDependencies'])); } public function testLoadFixturesViaGroupWithFulfilledDependency(): void