From 8c2a866a6827ca621204262b7a69bed1fa8cc632 Mon Sep 17 00:00:00 2001 From: Francis Hilaire Date: Fri, 7 Jul 2023 13:26:21 +0200 Subject: [PATCH] Drivers can be configured --- psalm.xml | 2 + .../DependencyInjection/Configuration.php | 77 ++++++++++++++++++- .../Driver/DriverProvider.php | 28 +++---- .../SyliusResourceExtension.php | 41 ++++++---- .../SyliusResourceExtensionTest.php | 71 +++++++++++++++++ 5 files changed, 185 insertions(+), 34 deletions(-) diff --git a/psalm.xml b/psalm.xml index e84b157eb..799cf38dc 100644 --- a/psalm.xml +++ b/psalm.xml @@ -150,9 +150,11 @@ + + diff --git a/src/Bundle/DependencyInjection/Configuration.php b/src/Bundle/DependencyInjection/Configuration.php index 2867b9f0f..46c38de64 100644 --- a/src/Bundle/DependencyInjection/Configuration.php +++ b/src/Bundle/DependencyInjection/Configuration.php @@ -14,12 +14,17 @@ namespace Sylius\Bundle\ResourceBundle\DependencyInjection; use Sylius\Bundle\ResourceBundle\Controller\ResourceController; +use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Doctrine\DoctrineODMDriver; +use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Doctrine\DoctrineORMDriver; +use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Doctrine\DoctrinePHPCRDriver; +use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\DriverInterface; use Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType; use Sylius\Bundle\ResourceBundle\SyliusResourceBundle; use Sylius\Component\Resource\Factory\Factory; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; final class Configuration implements ConfigurationInterface { @@ -146,8 +151,76 @@ private function addDriversSection(ArrayNodeDefinition $node): void $node ->children() ->arrayNode('drivers') - ->defaultValue([SyliusResourceBundle::DRIVER_DOCTRINE_ORM]) - ->enumPrototype()->values(SyliusResourceBundle::getAvailableDrivers())->end() + ->info('The list of enabled drivers with there related class.') + ->normalizeKeys(false) // do not replace dashes with underscores + ->useAttributeAsKey('type') + ->defaultValue([ + SyliusResourceBundle::DRIVER_DOCTRINE_ORM => ['class' => DoctrineORMDriver::class], + ]) + ->beforeNormalization() + ->ifArray() + ->then(static function (array $v) { + foreach ($v as $driver => $value) { + if (isset($value['class'])) { + continue; + } + // retro-compatibility + if (in_array($value, SyliusResourceBundle::getAvailableDrivers(), true)) { + $v[$value] = ['class' => match ($value) { + SyliusResourceBundle::DRIVER_DOCTRINE_ORM => DoctrineORMDriver::class, + SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM => DoctrineODMDriver::class, + SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM => DoctrinePHPCRDriver::class, + }]; + + unset($v[$driver]); + + continue; + } + + $v[$driver] = ['class' => $value]; + } + + return $v; + }) + ->end() + ->validate() + ->ifArray() + ->then(static function (array $v) { + foreach ($v as $driver => $value) { + /** @var string|null $class */ + $class = $value['class'] ?? null; + if (null === $class) { + throw new InvalidConfigurationException(sprintf( + 'The Sylius Resource driver "%s" must have a class!', + $driver, + )); + } + + if (false === class_exists($class)) { + throw new InvalidConfigurationException(sprintf( + 'The Sylius Resource driver "%s" class must be an existing class, "%s" given.', + $driver, + $class, + )); + } + + if (false === is_a($class, DriverInterface::class, true)) { + throw new InvalidConfigurationException(sprintf( + 'The Sylius Resource driver "%s" class must be an instance of "%s".', + $driver, + DriverInterface::class, + )); + } + } + + return $v; + }) + ->end() + ->prototype('array') + ->children() + ->scalarNode('class')->cannotBeEmpty()->end() + ->end() + ->end() ->end() ->end() ; diff --git a/src/Bundle/DependencyInjection/Driver/DriverProvider.php b/src/Bundle/DependencyInjection/Driver/DriverProvider.php index cac2af464..eb6d1505d 100644 --- a/src/Bundle/DependencyInjection/Driver/DriverProvider.php +++ b/src/Bundle/DependencyInjection/Driver/DriverProvider.php @@ -13,19 +13,20 @@ namespace Sylius\Bundle\ResourceBundle\DependencyInjection\Driver; -use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Doctrine\DoctrineODMDriver; -use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Doctrine\DoctrineORMDriver; -use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Doctrine\DoctrinePHPCRDriver; use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Exception\UnknownDriverException; -use Sylius\Bundle\ResourceBundle\SyliusResourceBundle; use Sylius\Component\Resource\Metadata\MetadataInterface; use Webmozart\Assert\Assert; final class DriverProvider { - /** @var DriverInterface[] */ + /** @var array> */ private static array $drivers = []; + public static function build(array $drivers): void + { + self::$drivers = $drivers; + } + /** * @throws UnknownDriverException */ @@ -33,21 +34,16 @@ public static function get(MetadataInterface $metadata): DriverInterface { $type = $metadata->getDriver(); - if (isset(self::$drivers[$type])) { - return self::$drivers[$type]; + /** @var class-string|null $class */ + $class = self::$drivers[$type]['class'] ?? null; + if (null !== $class) { + $driver = new $class(); + Assert::isInstanceOf($driver, DriverInterface::class); + return $driver; } Assert::notFalse($type, sprintf('No driver was configured on the resource "%s".', $metadata->getAlias())); - switch ($type) { - case SyliusResourceBundle::DRIVER_DOCTRINE_ORM: - return self::$drivers[$type] = new DoctrineORMDriver(); - case SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM: - return self::$drivers[$type] = new DoctrineODMDriver(); - case SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM: - return self::$drivers[$type] = new DoctrinePHPCRDriver(); - } - throw new UnknownDriverException($type); } } diff --git a/src/Bundle/DependencyInjection/SyliusResourceExtension.php b/src/Bundle/DependencyInjection/SyliusResourceExtension.php index d21456969..df7eac5c6 100644 --- a/src/Bundle/DependencyInjection/SyliusResourceExtension.php +++ b/src/Bundle/DependencyInjection/SyliusResourceExtension.php @@ -169,22 +169,44 @@ private function getResourceAlias(Resource $resource, string $className): string return 'app.' . u($shortName)->snake()->toString(); } + /** + * @param array> $drivers + */ private function loadPersistence(array $drivers, array $resources, LoaderInterface $loader): void { - $integrateDoctrine = array_reduce($drivers, function (bool $result, string $driver): bool { - return $result || in_array($driver, [SyliusResourceBundle::DRIVER_DOCTRINE_ORM, SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM], true); + DriverProvider::build($drivers); + + $integrateDoctrine = array_reduce(array_keys($drivers), static function (bool $result, string $type): bool { + return $result || in_array($type, [SyliusResourceBundle::DRIVER_DOCTRINE_ORM, SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM], true); }, false); if ($integrateDoctrine) { $loader->load('services/integrations/doctrine.xml'); } + foreach ($drivers as $type => $driver) { + if (in_array($type, [SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM], true)) { + trigger_deprecation( + 'sylius/resource-bundle', + '1.3', + 'The "%s" driver is deprecated. Doctrine MongoDB and PHPCR will no longer be supported in 2.0.', + $type, + ); + } + + if (false === in_array($type, SyliusResourceBundle::getAvailableDrivers(), true)) { + continue; + } + + $loader->load(sprintf('services/integrations/%s.xml', $type)); + } + foreach ($resources as $alias => $resource) { if (false === $resource['driver']) { break; } - if (!in_array($resource['driver'], $drivers, true)) { + if (!array_key_exists($resource['driver'], $drivers)) { throw new InvalidArgumentException(sprintf( 'Resource "%s" uses driver "%s", but this driver has not been enabled.', $alias, @@ -192,19 +214,6 @@ private function loadPersistence(array $drivers, array $resources, LoaderInterfa )); } } - - foreach ($drivers as $driver) { - if (in_array($driver, [SyliusResourceBundle::DRIVER_DOCTRINE_PHPCR_ODM, SyliusResourceBundle::DRIVER_DOCTRINE_MONGODB_ODM], true)) { - trigger_deprecation( - 'sylius/resource-bundle', - '1.3', - 'The "%s" driver is deprecated. Doctrine MongoDB and PHPCR will no longer be supported in 2.0.', - $driver, - ); - } - - $loader->load(sprintf('services/integrations/%s.xml', $driver)); - } } private function loadResources(array $loadedResources, ContainerBuilder $container): void diff --git a/src/Bundle/Tests/DependencyInjection/SyliusResourceExtensionTest.php b/src/Bundle/Tests/DependencyInjection/SyliusResourceExtensionTest.php index 258aca1f5..acfdc1f40 100644 --- a/src/Bundle/Tests/DependencyInjection/SyliusResourceExtensionTest.php +++ b/src/Bundle/Tests/DependencyInjection/SyliusResourceExtensionTest.php @@ -19,6 +19,7 @@ use App\Factory\BookFactory; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase; use Sylius\Bundle\ResourceBundle\Controller\ResourceController; +use Sylius\Bundle\ResourceBundle\DependencyInjection\Driver\Doctrine\DoctrineORMDriver; use Sylius\Bundle\ResourceBundle\DependencyInjection\SyliusResourceExtension; use Sylius\Bundle\ResourceBundle\Form\Type\DefaultResourceType; use Sylius\Bundle\ResourceBundle\Tests\DependencyInjection\Dummy\BookWithAliasResource; @@ -179,6 +180,76 @@ public function it_auto_registers_resources(): void ]); } + /** + * @test + */ + public function it_allows_registering_custom_driver(): void + { + $this->setParameter('kernel.bundles', []); + $this->load([ + 'drivers' => [ + 'custom' => [ + 'class' => DoctrineORMDriver::class, + ], + ], + ]); + + // services/integrations/doctrine.xml not loaded + $this->assertContainerBuilderNotHasService('sylius_resource.doctrine.mapping_driver_chain'); + // services/integrations/doctrine/orm.xml not loaded + $this->assertContainerBuilderNotHasService('sylius.event_subscriber.orm_mapped_super_class'); + } + + /** + * @test + */ + public function it_allows_registering_custom_driver_with_simple_array_values(): void + { + $this->setParameter('kernel.bundles', []); + $this->load([ + 'drivers' => [ + 'custom' => DoctrineORMDriver::class, + ], + ]); + + // services/integrations/doctrine.xml not loaded + $this->assertContainerBuilderNotHasService('sylius_resource.doctrine.mapping_driver_chain'); + // services/integrations/doctrine/orm.xml not loaded + $this->assertContainerBuilderNotHasService('sylius.event_subscriber.orm_mapped_super_class'); + } + + /** + * @test + */ + public function it_allows_registering_drivers_with_the_legacy_string_array(): void + { + $this->setParameter('kernel.bundles', []); + $this->load([ + 'drivers' => [ + 'doctrine/orm', + ], + ]); + + // services/integrations/doctrine.xml is loaded + $this->assertContainerBuilderHasService('sylius_resource.doctrine.mapping_driver_chain'); + // services/integrations/doctrine/orm.xml is loaded + $this->assertContainerBuilderHasService('sylius.event_subscriber.orm_mapped_super_class'); + } + + /** + * @test + */ + public function it_allows_registering_drivers_with_defaults_values(): void + { + $this->setParameter('kernel.bundles', []); + $this->load(); + + // services/integrations/doctrine.xml is loaded + $this->assertContainerBuilderHasService('sylius_resource.doctrine.mapping_driver_chain'); + // services/integrations/doctrine/orm.xml is loaded + $this->assertContainerBuilderHasService('sylius.event_subscriber.orm_mapped_super_class'); + } + protected function getContainerExtensions(): array { return [