From 8e5699d3b00908912b629e01095e23d86fbf3a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Wr=C3=B3blewski?= Date: Sun, 11 Feb 2024 00:34:48 +0100 Subject: [PATCH] feat: simplify exporters - removed exporter extensions, registry directly working with container --- src/AbstractDependencyInjectionExtension.php | 71 -------- src/AbstractExtension.php | 133 --------------- src/AbstractRegistry.php | 152 ------------------ src/DataTables.php | 26 --- .../KreyuDataTableExtension.php | 3 - src/Exporter/ExporterFactoryBuilder.php | 88 ---------- .../ExporterFactoryBuilderInterface.php | 38 ----- src/Exporter/ExporterRegistry.php | 101 ++++++++++-- src/Exporter/ExporterRegistryInterface.php | 5 +- .../Extension/AbstractExporterExtension.php | 34 ---- .../DependencyInjectionExporterExtension.php | 27 ---- .../Extension/ExporterExtensionInterface.php | 18 --- .../Extension/PreloadedExporterExtension.php | 30 ---- src/Resources/config/actions.php | 3 +- src/Resources/config/exporter.php | 3 +- src/Resources/config/extensions.php | 22 --- src/Resources/config/filtration.php | 3 +- .../Exporter/ExporterIntegrationTestCase.php | 20 ++- src/Test/Filter/FilterIntegrationTestCase.php | 2 - .../SimpleExporterTypeBarExtension.php | 16 ++ .../SimpleExporterTypeFooExtension.php | 16 ++ .../Type/ConfigurableExporterType.php | 28 ++++ .../Type/ExporterTypeWithSameParentType.php | 24 +++ .../Type/RecursiveExporterTypeBar.php | 24 +++ .../Type/RecursiveExporterTypeBaz.php | 24 +++ .../Type/RecursiveExporterTypeFoo.php | 24 +++ .../Exporter/Type/SimpleExporterType.php | 19 +++ .../Exporter/Type/SimpleSubExporterType.php | 24 +++ tests/Unit/Exporter/ExporterFactoryTest.php | 75 +++++++++ tests/Unit/Exporter/ExporterRegistryTest.php | 131 +++++++++++++++ 30 files changed, 513 insertions(+), 671 deletions(-) delete mode 100644 src/AbstractDependencyInjectionExtension.php delete mode 100644 src/AbstractExtension.php delete mode 100644 src/AbstractRegistry.php delete mode 100644 src/DataTables.php delete mode 100644 src/Exporter/ExporterFactoryBuilder.php delete mode 100644 src/Exporter/ExporterFactoryBuilderInterface.php delete mode 100644 src/Exporter/Extension/AbstractExporterExtension.php delete mode 100644 src/Exporter/Extension/DependencyInjection/DependencyInjectionExporterExtension.php delete mode 100644 src/Exporter/Extension/ExporterExtensionInterface.php delete mode 100644 src/Exporter/Extension/PreloadedExporterExtension.php delete mode 100755 src/Resources/config/extensions.php create mode 100644 tests/Fixtures/Exporter/Extension/SimpleExporterTypeBarExtension.php create mode 100644 tests/Fixtures/Exporter/Extension/SimpleExporterTypeFooExtension.php create mode 100644 tests/Fixtures/Exporter/Type/ConfigurableExporterType.php create mode 100644 tests/Fixtures/Exporter/Type/ExporterTypeWithSameParentType.php create mode 100644 tests/Fixtures/Exporter/Type/RecursiveExporterTypeBar.php create mode 100644 tests/Fixtures/Exporter/Type/RecursiveExporterTypeBaz.php create mode 100644 tests/Fixtures/Exporter/Type/RecursiveExporterTypeFoo.php create mode 100644 tests/Fixtures/Exporter/Type/SimpleExporterType.php create mode 100644 tests/Fixtures/Exporter/Type/SimpleSubExporterType.php create mode 100644 tests/Unit/Exporter/ExporterFactoryTest.php create mode 100644 tests/Unit/Exporter/ExporterRegistryTest.php diff --git a/src/AbstractDependencyInjectionExtension.php b/src/AbstractDependencyInjectionExtension.php deleted file mode 100644 index caaf5685..00000000 --- a/src/AbstractDependencyInjectionExtension.php +++ /dev/null @@ -1,71 +0,0 @@ -typeContainer->has($name)) { - throw new InvalidArgumentException(sprintf('The %s type "%s" is not registered in the service container.', $this->getErrorContextName(), $name)); - } - - return $this->typeContainer->get($name); - } - - public function hasType(string $name): bool - { - return $this->typeContainer->has($name); - } - - public function getTypeExtensions(string $name): array - { - $extensions = []; - - if (isset($this->typeExtensionServices[$name])) { - foreach ($this->typeExtensionServices[$name] as $extension) { - $extensions[] = $extension; - - $extendedTypes = []; - foreach ($extension::getExtendedTypes() as $extendedType) { - $extendedTypes[] = $extendedType; - } - - // validate the result of getExtendedTypes() to ensure it is consistent with the service definition - if (!\in_array($name, $extendedTypes, true)) { - throw new InvalidArgumentException(sprintf('The extended %s type "%s" specified for the type extension class "%s" does not match any of the actual extended types (["%s"]).', $this->getErrorContextName(), $name, $extension::class, implode('", "', $extendedTypes))); - } - } - } - - return $extensions; - } - - public function hasTypeExtensions(string $name): bool - { - return isset($this->typeExtensionServices[$name]); - } - - /** - * @return class-string - */ - abstract protected function getTypeClass(): string; - - abstract protected function getErrorContextName(): string; -} diff --git a/src/AbstractExtension.php b/src/AbstractExtension.php deleted file mode 100644 index e0357834..00000000 --- a/src/AbstractExtension.php +++ /dev/null @@ -1,133 +0,0 @@ - - */ - private array $types; - - /** - * @var array> - */ - private array $typeExtensions; - - public function hasType(string $name): bool - { - if (!isset($this->types)) { - $this->initTypes(); - } - - return isset($this->types[$name]); - } - - public function getTypeExtensions(string $name): array - { - if (!isset($this->typeExtensions)) { - $this->initTypeExtensions(); - } - - return $this->typeExtensions[$name] ?? []; - } - - public function hasTypeExtensions(string $name): bool - { - if (!isset($this->typeExtensions)) { - $this->initTypeExtensions(); - } - - return isset($this->typeExtensions[$name]) && \count($this->typeExtensions[$name]) > 0; - } - - /** - * @return array - */ - protected function loadTypes(): array - { - return []; - } - - /** - * @return array - */ - protected function loadTypeExtensions(): array - { - return []; - } - - /** - * @return class-string - */ - abstract protected function getTypeClass(): string; - - /** - * @return class-string - */ - abstract protected function getTypeExtensionClass(): string; - - abstract protected function getErrorContextName(): string; - - protected function doGetType(string $name) - { - if (!isset($this->types)) { - $this->initTypes(); - } - - if (!isset($this->types[$name])) { - throw new InvalidArgumentException(sprintf('The %s type "%s" cannot be loaded by this extension.', $this->getErrorContextName(), $name)); - } - - return $this->types[$name]; - } - - private function initTypes(): void - { - $this->types = []; - - $typeClass = $this->getTypeClass(); - - foreach ($this->loadTypes() as $type) { - if (!$type instanceof $typeClass) { - throw new UnexpectedTypeException($type, $typeClass); - } - - $this->types[$type::class] = $type; - } - } - - private function initTypeExtensions(): void - { - $this->typeExtensions = []; - - $typeExtensionClass = $this->getTypeExtensionClass(); - - foreach ($this->loadTypeExtensions() as $extensions) { - if (!is_array($extensions)) { - $extensions = [$extensions]; - } - - foreach ($extensions as $extension) { - if (!$extension instanceof $typeExtensionClass) { - throw new UnexpectedTypeException($extension, $typeExtensionClass); - } - - foreach ($extension::getExtendedTypes() as $extendedType) { - $this->typeExtensions[$extendedType][] = $extension; - } - } - } - } -} diff --git a/src/AbstractRegistry.php b/src/AbstractRegistry.php deleted file mode 100644 index 60de0938..00000000 --- a/src/AbstractRegistry.php +++ /dev/null @@ -1,152 +0,0 @@ - - */ - private array $types = []; - - /** - * @var array, bool> - */ - private array $checkedTypes = []; - - /** - * @param iterable $extensions - */ - public function __construct( - private readonly iterable $extensions, - private readonly mixed $resolvedTypeFactory, - ) { - $extensionClass = $this->getExtensionClass(); - - foreach ($extensions as $extension) { - if (!$extension instanceof $extensionClass) { - throw new UnexpectedTypeException($extension, $extensionClass); - } - } - } - - public function hasType(string $name): bool - { - if (isset($this->types[$name])) { - return true; - } - - try { - $this->doGetType($name); - } catch (ExceptionInterface) { - return false; - } - - return true; - } - - /** - * @return iterable - */ - public function getExtensions(): iterable - { - return $this->extensions; - } - - /** - * @return TResolvedType - */ - protected function doGetType(string $name) - { - if (!isset($this->types[$name])) { - $type = null; - - foreach ($this->extensions as $extension) { - if ($extension->hasType($name)) { - $type = $extension->getType($name); - break; - } - } - - if (!$type) { - $typeClass = $this->getTypeClass(); - - if (!class_exists($name)) { - throw new InvalidArgumentException(sprintf('Could not load %s type "%s": class does not exist.', $this->getErrorContextName(), $name)); - } - - if (!is_subclass_of($name, $typeClass)) { - throw new InvalidArgumentException(sprintf('Could not load %s type "%s": class does not implement "%s".', $this->getErrorContextName(), $name, $typeClass)); - } - - $type = new $name(); - } - - $this->types[$name] = $this->resolveType($type); - } - - return $this->types[$name]; - } - - /** - * @return TResolvedType - */ - private function resolveType($type) - { - $parentType = $type->getParent(); - $fqcn = $type::class; - - if (isset($this->checkedTypes[$fqcn])) { - $types = implode(' > ', array_merge(array_keys($this->checkedTypes), [$fqcn])); - throw new \LogicException(sprintf('Circular reference detected for %s type "%s" (%s).', $this->getErrorContextName(), $fqcn, $types)); - } - - $this->checkedTypes[$fqcn] = true; - - $typeExtensions = []; - - try { - foreach ($this->extensions as $extension) { - $typeExtensions[] = $extension->getTypeExtensions($fqcn); - } - - return $this->resolvedTypeFactory->createResolvedType( - $type, - array_merge([], ...$typeExtensions), - $parentType ? $this->getType($parentType) : null, - ); - } finally { - unset($this->checkedTypes[$fqcn]); - } - } - - /** - * @return TResolvedType - */ - abstract public function getType(string $name); - - /** - * @return class-string - */ - abstract protected function getTypeClass(): string; - - /** - * @return class-string - */ - abstract protected function getExtensionClass(): string; - - abstract protected function getErrorContextName(): string; -} diff --git a/src/DataTables.php b/src/DataTables.php deleted file mode 100644 index a6ae0f9b..00000000 --- a/src/DataTables.php +++ /dev/null @@ -1,26 +0,0 @@ -getExporterFactory(); - } - - public static function createExporterFactoryBuilder(): ExporterFactoryBuilderInterface - { - return new ExporterFactoryBuilder(); - } - - private function __construct() - { - } -} diff --git a/src/DependencyInjection/KreyuDataTableExtension.php b/src/DependencyInjection/KreyuDataTableExtension.php index 8da73a6e..7c9cd35c 100755 --- a/src/DependencyInjection/KreyuDataTableExtension.php +++ b/src/DependencyInjection/KreyuDataTableExtension.php @@ -8,7 +8,6 @@ use Kreyu\Bundle\DataTableBundle\Action\Type\ActionTypeInterface; use Kreyu\Bundle\DataTableBundle\Column\Extension\ColumnTypeExtensionInterface; use Kreyu\Bundle\DataTableBundle\Column\Type\ColumnTypeInterface; -use Kreyu\Bundle\DataTableBundle\Exporter\Extension\ExporterExtensionInterface; use Kreyu\Bundle\DataTableBundle\Exporter\Extension\ExporterTypeExtensionInterface; use Kreyu\Bundle\DataTableBundle\Exporter\Type\ExporterTypeInterface; use Kreyu\Bundle\DataTableBundle\Extension\DataTableTypeExtensionInterface; @@ -36,7 +35,6 @@ class KreyuDataTableExtension extends Extension implements PrependExtensionInter FilterTypeExtensionInterface::class => 'kreyu_data_table.filter.type_extension', ActionTypeInterface::class => 'kreyu_data_table.action.type', ActionTypeExtensionInterface::class => 'kreyu_data_table.action.type_extension', - ExporterExtensionInterface::class => 'kreyu_data_table.exporter.extension', ExporterTypeInterface::class => 'kreyu_data_table.exporter.type', ExporterTypeExtensionInterface::class => 'kreyu_data_table.exporter.type_extension', PersistenceAdapterInterface::class => 'kreyu_data_table.persistence.adapter', @@ -53,7 +51,6 @@ public function load(array $configs, ContainerBuilder $container): void $loader->load('core.php'); $loader->load('actions.php'); $loader->load('exporter.php'); - $loader->load('extensions.php'); $loader->load('filtration.php'); $loader->load('personalization.php'); $loader->load('twig.php'); diff --git a/src/Exporter/ExporterFactoryBuilder.php b/src/Exporter/ExporterFactoryBuilder.php deleted file mode 100644 index cb7de3b8..00000000 --- a/src/Exporter/ExporterFactoryBuilder.php +++ /dev/null @@ -1,88 +0,0 @@ -resolvedTypeFactory = $resolvedTypeFactory; - - return $this; - } - - public function addExtension(ExporterExtensionInterface $extension): static - { - $this->extensions[] = $extension; - - return $this; - } - - public function addExtensions(array $extensions): static - { - $this->extensions = array_merge($this->extensions, $extensions); - - return $this; - } - - public function addType(ExporterTypeInterface $type): static - { - $this->types[] = $type; - - return $this; - } - - public function addTypes(array $types): static - { - foreach ($types as $type) { - $this->types[] = $type; - } - - return $this; - } - - public function addTypeExtension(ExporterTypeExtensionInterface $typeExtension): static - { - foreach ($typeExtension::getExtendedTypes() as $extendedType) { - $this->typeExtensions[$extendedType][] = $typeExtension; - } - - return $this; - } - - public function addTypeExtensions(array $typeExtensions): static - { - foreach ($typeExtensions as $typeExtension) { - $this->addTypeExtension($typeExtension); - } - - return $this; - } - - public function getExporterFactory(): ExporterFactoryInterface - { - $extensions = $this->extensions; - - if (\count($this->types) > 0 || \count($this->typeExtensions) > 0) { - $extensions[] = new PreloadedExporterExtension($this->types, $this->typeExtensions); - } - - $registry = new ExporterRegistry($extensions, $this->resolvedTypeFactory ?? new ResolvedExporterTypeFactory()); - - return new ExporterFactory($registry); - } -} diff --git a/src/Exporter/ExporterFactoryBuilderInterface.php b/src/Exporter/ExporterFactoryBuilderInterface.php deleted file mode 100644 index 9c5ddf62..00000000 --- a/src/Exporter/ExporterFactoryBuilderInterface.php +++ /dev/null @@ -1,38 +0,0 @@ - $extensions - */ - public function addExtensions(array $extensions): static; - - public function addType(ExporterTypeInterface $type): static; - - /** - * @param array $types - */ - public function addTypes(array $types): static; - - public function addTypeExtension(ExporterTypeExtensionInterface $typeExtension): static; - - /** - * @param array $typeExtensions - */ - public function addTypeExtensions(array $typeExtensions): static; - - public function getExporterFactory(): ExporterFactoryInterface; -} diff --git a/src/Exporter/ExporterRegistry.php b/src/Exporter/ExporterRegistry.php index 766c87be..8d6818ee 100755 --- a/src/Exporter/ExporterRegistry.php +++ b/src/Exporter/ExporterRegistry.php @@ -4,33 +4,108 @@ namespace Kreyu\Bundle\DataTableBundle\Exporter; -use Kreyu\Bundle\DataTableBundle\AbstractRegistry; -use Kreyu\Bundle\DataTableBundle\Exporter\Extension\ExporterExtensionInterface; +use Kreyu\Bundle\DataTableBundle\Exporter\Extension\ExporterTypeExtensionInterface; use Kreyu\Bundle\DataTableBundle\Exporter\Type\ExporterTypeInterface; +use Kreyu\Bundle\DataTableBundle\Exporter\Type\ResolvedExporterTypeFactoryInterface; use Kreyu\Bundle\DataTableBundle\Exporter\Type\ResolvedExporterTypeInterface; +use Kreyu\Bundle\DataTableBundle\Exception\InvalidArgumentException; +use Kreyu\Bundle\DataTableBundle\Exception\LogicException; +use Kreyu\Bundle\DataTableBundle\Exception\UnexpectedTypeException; -/** - * @extends AbstractRegistry - */ -class ExporterRegistry extends AbstractRegistry implements ExporterRegistryInterface +class ExporterRegistry implements ExporterRegistryInterface { + /** + * @var array + */ + private array $types; + + /** + * @var array + */ + private array $typeExtensions; + + /** + * @var array + */ + private array $resolvedTypes; + + /** + * @var array, bool> + */ + private array $checkedTypes; + + /** + * @param iterable $types + * @param iterable $typeExtensions + */ + public function __construct( + iterable $types, + iterable $typeExtensions, + private readonly ResolvedExporterTypeFactoryInterface $resolvedTypeFactory, + ) { + $this->setTypes($types); + $this->setTypeExtensions($typeExtensions); + } + public function getType(string $name): ResolvedExporterTypeInterface { - return $this->doGetType($name); + return $this->resolvedTypes[$name] ??= $this->resolveType($name); } - final protected function getErrorContextName(): string + public function hasType(string $name): bool { - return 'exporter'; + return isset($this->types[$name]); } - final protected function getTypeClass(): string + private function resolveType(string $name): ResolvedExporterTypeInterface { - return ExporterTypeInterface::class; + $type = $this->types[$name] ?? throw new InvalidArgumentException(sprintf('The exporter type %s does not exist', $name)); + + if (isset($this->checkedTypes[$fqcn = $type::class])) { + $types = implode(' > ', array_merge(array_keys($this->checkedTypes), [$fqcn])); + throw new LogicException(sprintf('Circular reference detected for exporter type "%s" (%s).', $fqcn, $types)); + } + + $this->checkedTypes[$fqcn] = true; + + $parentType = $type->getParent(); + + try { + return $this->resolvedTypeFactory->createResolvedType( + type: $type, + typeExtensions: $this->typeExtensions[$type::class] ?? [], + parent: $parentType ? $this->getType($parentType) : null, + ); + } finally { + unset($this->checkedTypes[$fqcn]); + } + } + + private function setTypes(iterable $types): void + { + $this->types = []; + + foreach ($types as $type) { + if (!$type instanceof ExporterTypeInterface) { + throw new UnexpectedTypeException($type, ExporterTypeInterface::class); + } + + $this->types[$type::class] = $type; + } } - final protected function getExtensionClass(): string + private function setTypeExtensions(iterable $typeExtensions): void { - return ExporterExtensionInterface::class; + $this->typeExtensions = []; + + foreach ($typeExtensions as $typeExtension) { + if (!$typeExtension instanceof ExporterTypeExtensionInterface) { + throw new UnexpectedTypeException($typeExtension, ExporterTypeExtensionInterface::class); + } + + foreach ($typeExtension::getExtendedTypes() as $extendedType) { + $this->typeExtensions[$extendedType][] = $typeExtension; + } + } } } diff --git a/src/Exporter/ExporterRegistryInterface.php b/src/Exporter/ExporterRegistryInterface.php index dc714593..dd7ce09a 100755 --- a/src/Exporter/ExporterRegistryInterface.php +++ b/src/Exporter/ExporterRegistryInterface.php @@ -4,7 +4,6 @@ namespace Kreyu\Bundle\DataTableBundle\Exporter; -use Kreyu\Bundle\DataTableBundle\Exporter\Extension\ExporterExtensionInterface; use Kreyu\Bundle\DataTableBundle\Exporter\Type\ExporterTypeInterface; use Kreyu\Bundle\DataTableBundle\Exporter\Type\ResolvedExporterTypeInterface; @@ -16,7 +15,7 @@ interface ExporterRegistryInterface public function getType(string $name): ResolvedExporterTypeInterface; /** - * @return iterable + * @param class-string $name */ - public function getExtensions(): iterable; + public function hasType(string $name): bool; } diff --git a/src/Exporter/Extension/AbstractExporterExtension.php b/src/Exporter/Extension/AbstractExporterExtension.php deleted file mode 100644 index bfdd58fb..00000000 --- a/src/Exporter/Extension/AbstractExporterExtension.php +++ /dev/null @@ -1,34 +0,0 @@ - - */ -abstract class AbstractExporterExtension extends AbstractExtension implements ExporterExtensionInterface -{ - public function getType(string $name): ExporterTypeInterface - { - return $this->doGetType($name); - } - - final protected function getErrorContextName(): string - { - return 'exporter'; - } - - final protected function getTypeClass(): string - { - return ExporterTypeInterface::class; - } - - final protected function getTypeExtensionClass(): string - { - return ExporterTypeExtensionInterface::class; - } -} diff --git a/src/Exporter/Extension/DependencyInjection/DependencyInjectionExporterExtension.php b/src/Exporter/Extension/DependencyInjection/DependencyInjectionExporterExtension.php deleted file mode 100644 index 43d88c41..00000000 --- a/src/Exporter/Extension/DependencyInjection/DependencyInjectionExporterExtension.php +++ /dev/null @@ -1,27 +0,0 @@ -doGetType($name); - } - - protected function getTypeClass(): string - { - return ExporterTypeInterface::class; - } - - protected function getErrorContextName(): string - { - return 'filter'; - } -} diff --git a/src/Exporter/Extension/ExporterExtensionInterface.php b/src/Exporter/Extension/ExporterExtensionInterface.php deleted file mode 100644 index 3ddd4dd3..00000000 --- a/src/Exporter/Extension/ExporterExtensionInterface.php +++ /dev/null @@ -1,18 +0,0 @@ - $types - * @param array|array> $typeExtensions - */ - public function __construct( - private readonly array $types = [], - private readonly array $typeExtensions = [], - ) { - } - - protected function loadTypes(): array - { - return $this->types; - } - - protected function loadTypeExtensions(): array - { - return $this->typeExtensions; - } -} diff --git a/src/Resources/config/actions.php b/src/Resources/config/actions.php index 58249fdd..5f76912b 100755 --- a/src/Resources/config/actions.php +++ b/src/Resources/config/actions.php @@ -28,7 +28,8 @@ $services ->set('kreyu_data_table.action.registry', ActionRegistry::class) ->args([ - tagged_iterator('kreyu_data_table.action.extension'), + tagged_iterator('kreyu_data_table.action.type'), + tagged_iterator('kreyu_data_table.action.type_extension'), service('kreyu_data_table.action.resolved_type_factory'), ]) ->alias(ActionRegistryInterface::class, 'kreyu_data_table.action.registry') diff --git a/src/Resources/config/exporter.php b/src/Resources/config/exporter.php index 7359e345..b89f7706 100755 --- a/src/Resources/config/exporter.php +++ b/src/Resources/config/exporter.php @@ -35,7 +35,8 @@ $services ->set('kreyu_data_table.exporter.registry', ExporterRegistry::class) ->args([ - tagged_iterator('kreyu_data_table.exporter.extension'), + tagged_iterator('kreyu_data_table.exporter.type'), + tagged_iterator('kreyu_data_table.exporter.type_extension'), service('kreyu_data_table.exporter.resolved_type_factory'), ]) ->alias(ExporterRegistryInterface::class, 'kreyu_data_table.exporter.registry') diff --git a/src/Resources/config/extensions.php b/src/Resources/config/extensions.php deleted file mode 100755 index b90d8f78..00000000 --- a/src/Resources/config/extensions.php +++ /dev/null @@ -1,22 +0,0 @@ -services() - ->set('kreyu_data_table.exporter.extension', DependencyInjectionExporterExtension::class) - ->args([ - abstract_arg('All services with tag "kreyu_data_table.exporter.type" are stored in a service locator by DataTablePass'), - abstract_arg('All services with tag "kreyu_data_table.exporter.type_extension" are stored here by DataTablePass'), - ]) - ->tag('kreyu_data_table.exporter.extension', [ - 'type' => 'kreyu_data_table.exporter.type', - 'type_extension' => 'kreyu_data_table.exporter.type_extension', - ]) - ; -}; diff --git a/src/Resources/config/filtration.php b/src/Resources/config/filtration.php index 78f126f9..42ef2e2e 100755 --- a/src/Resources/config/filtration.php +++ b/src/Resources/config/filtration.php @@ -54,7 +54,8 @@ $services ->set('kreyu_data_table.filter.registry', FilterRegistry::class) ->args([ - tagged_iterator('kreyu_data_table.filter.extension'), + tagged_iterator('kreyu_data_table.filter.type'), + tagged_iterator('kreyu_data_table.filter.type_extension'), service('kreyu_data_table.filter.resolved_type_factory'), ]) ->alias(FilterRegistryInterface::class, 'kreyu_data_table.filter.registry') diff --git a/src/Test/Exporter/ExporterIntegrationTestCase.php b/src/Test/Exporter/ExporterIntegrationTestCase.php index f92d256c..b1dc2c87 100644 --- a/src/Test/Exporter/ExporterIntegrationTestCase.php +++ b/src/Test/Exporter/ExporterIntegrationTestCase.php @@ -4,21 +4,25 @@ namespace Kreyu\Bundle\DataTableBundle\Test\Exporter; -use Kreyu\Bundle\DataTableBundle\DataTables; -use Kreyu\Bundle\DataTableBundle\Exporter\ExporterFactoryInterface; +use Kreyu\Bundle\DataTableBundle\Exporter\ExporterFactory; +use Kreyu\Bundle\DataTableBundle\Exporter\ExporterRegistry; +use Kreyu\Bundle\DataTableBundle\Exporter\Type\ResolvedExporterTypeFactory; use PHPUnit\Framework\TestCase; abstract class ExporterIntegrationTestCase extends TestCase { - protected ExporterFactoryInterface $factory; + protected ExporterRegistry $registry; + protected ExporterFactory $factory; protected function setUp(): void { - $this->factory = DataTables::createExporterFactoryBuilder() - ->addExtensions($this->getExtensions()) - ->addTypeExtensions($this->getTypeExtensions()) - ->addTypes($this->getTypes()) - ->getExporterFactory(); + $this->registry = new ExporterRegistry( + types: $this->getTypes(), + typeExtensions: $this->getTypeExtensions(), + resolvedTypeFactory: new ResolvedExporterTypeFactory(), + ); + + $this->factory = new ExporterFactory($this->registry); } protected function getExtensions(): array diff --git a/src/Test/Filter/FilterIntegrationTestCase.php b/src/Test/Filter/FilterIntegrationTestCase.php index 0e82b94e..96434740 100644 --- a/src/Test/Filter/FilterIntegrationTestCase.php +++ b/src/Test/Filter/FilterIntegrationTestCase.php @@ -4,9 +4,7 @@ namespace Kreyu\Bundle\DataTableBundle\Test\Filter; -use Kreyu\Bundle\DataTableBundle\DataTables; use Kreyu\Bundle\DataTableBundle\Filter\FilterFactory; -use Kreyu\Bundle\DataTableBundle\Filter\FilterFactoryInterface; use Kreyu\Bundle\DataTableBundle\Filter\FilterRegistry; use Kreyu\Bundle\DataTableBundle\Filter\Type\ResolvedFilterTypeFactory; use PHPUnit\Framework\TestCase; diff --git a/tests/Fixtures/Exporter/Extension/SimpleExporterTypeBarExtension.php b/tests/Fixtures/Exporter/Extension/SimpleExporterTypeBarExtension.php new file mode 100644 index 00000000..eb2867d9 --- /dev/null +++ b/tests/Fixtures/Exporter/Extension/SimpleExporterTypeBarExtension.php @@ -0,0 +1,16 @@ +setDefaults([ + 'foo' => null, + 'bar' => null, + ]); + } +} diff --git a/tests/Fixtures/Exporter/Type/ExporterTypeWithSameParentType.php b/tests/Fixtures/Exporter/Type/ExporterTypeWithSameParentType.php new file mode 100644 index 00000000..e02ac3d1 --- /dev/null +++ b/tests/Fixtures/Exporter/Type/ExporterTypeWithSameParentType.php @@ -0,0 +1,24 @@ +createFactory()->createNamedBuilder('name', ConfigurableExporterType::class, options: [ + 'foo' => 'a', + 'bar' => 'b', + ]); + + $this->assertSame('a', $builder->getOption('foo')); + $this->assertSame('b', $builder->getOption('bar')); + } + + public function testCreateBuilderUsesExporterName() + { + $builder = $this->createFactory()->createBuilder(SimpleExporterType::class); + + $this->assertSame('simple', $builder->getName()); + } + + public function testCreate() + { + $column = $this->createFactory()->create(ConfigurableExporterType::class, [ + 'foo' => 'a', + 'bar' => 'b', + ]); + + $this->assertSame('configurable', $column->getName()); + $this->assertSame('a', $column->getConfig()->getOption('foo')); + $this->assertSame('b', $column->getConfig()->getOption('bar')); + $this->assertInstanceOf(ConfigurableExporterType::class, $column->getConfig()->getType()->getInnerType()); + } + + public function testCreateNamed() + { + $column = $this->createFactory()->createNamed('name', ConfigurableExporterType::class, [ + 'foo' => 'a', + 'bar' => 'b', + ]); + + $this->assertSame('name', $column->getName()); + $this->assertSame('a', $column->getConfig()->getOption('foo')); + $this->assertSame('b', $column->getConfig()->getOption('bar')); + $this->assertInstanceOf(ConfigurableExporterType::class, $column->getConfig()->getType()->getInnerType()); + } + + private function createFactory(): ExporterFactory + { + $registry = new ExporterRegistry( + types: [ + new ExporterType(), + new SimpleExporterType(), + new ConfigurableExporterType(), + ], + typeExtensions: [], + resolvedTypeFactory: new ResolvedExporterTypeFactory(), + ); + + return new ExporterFactory($registry); + } +} \ No newline at end of file diff --git a/tests/Unit/Exporter/ExporterRegistryTest.php b/tests/Unit/Exporter/ExporterRegistryTest.php new file mode 100644 index 00000000..a8f6aee8 --- /dev/null +++ b/tests/Unit/Exporter/ExporterRegistryTest.php @@ -0,0 +1,131 @@ +createRegistry()->getType(SimpleExporterType::class); + + $this->assertInstanceOf(SimpleExporterType::class, $resolvedType->getInnerType()); + } + + public function testGetTypeWithNonExistentType() + { + $this->expectException(InvalidArgumentException::class); + + // @phpstan-ignore-next-line + $this->createRegistry()->getType('stdClass'); + } + + public function testGetTypeWithTypeExtensions() + { + $typeExtensions = [ + new SimpleExporterTypeFooExtension(), + new SimpleExporterTypeBarExtension(), + ]; + + $resolvedType = $this->createRegistry(typeExtensions: $typeExtensions)->getType(SimpleExporterType::class); + + $this->assertSame($typeExtensions, $resolvedType->getTypeExtensions()); + } + + public function testGetTypeWithParent() + { + $resolvedType = $this->createRegistry()->getType(SimpleSubExporterType::class); + + $this->assertInstanceOf(SimpleSubExporterType::class, $resolvedType->getInnerType()); + $this->assertInstanceOf(SimpleExporterType::class, $resolvedType->getParent()->getInnerType()); + } + + public function testGetTypeWithParentTypeExtensions() + { + $typeExtensions = [ + new SimpleExporterTypeFooExtension(), + new SimpleExporterTypeBarExtension(), + ]; + + $resolvedType = $this->createRegistry(typeExtensions: $typeExtensions)->getType(SimpleSubExporterType::class); + + $this->assertEmpty($resolvedType->getTypeExtensions()); + $this->assertSame($typeExtensions, $resolvedType->getParent()->getTypeExtensions()); + } + + public function testTypeCannotHaveItselfAsParent() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Circular reference detected for exporter type "Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\ExporterTypeWithSameParentType" (Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\ExporterTypeWithSameParentType > Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\ExporterTypeWithSameParentType).'); + + $registry = $this->createRegistry(types: [new ExporterTypeWithSameParentType()]); + $registry->getType(ExporterTypeWithSameParentType::class); + } + + public function testRecursiveTypeReferences() + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Circular reference detected for exporter type "Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\RecursiveExporterTypeFoo" (Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\RecursiveExporterTypeFoo > Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\RecursiveExporterTypeBar > Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\RecursiveExporterTypeBaz > Kreyu\Bundle\DataTableBundle\Tests\Fixtures\Exporter\Type\RecursiveExporterTypeFoo).'); + + $registry = $this->createRegistry(types: [ + new RecursiveExporterTypeFoo(), + new RecursiveExporterTypeBar(), + new RecursiveExporterTypeBaz(), + ]); + + $registry->getType(RecursiveExporterTypeFoo::class); + } + + public function testHasType() + { + $this->assertTrue($this->createRegistry()->hasType(SimpleExporterType::class)); + } + + public function testHasTypeWithNonExistentType() + { + // @phpstan-ignore-next-line + $this->assertFalse($this->createRegistry()->hasType('stdClass')); + } + + public function testCreatingRegistryWithInvalidType() + { + $this->expectException(UnexpectedTypeException::class); + $this->createRegistry(types: [new \stdClass()]); + } + + public function testCreatingRegistryWithInvalidTypeExtension() + { + $this->expectException(UnexpectedTypeException::class); + $this->createRegistry(typeExtensions: [new \stdClass()]); + } + + private function createRegistry(array $types = [], array $typeExtensions = []): ExporterRegistry + { + return new ExporterRegistry( + types: $types ?: [ + new ExporterType(), + new SimpleExporterType(), + new SimpleSubExporterType(), + ], + typeExtensions: $typeExtensions, + resolvedTypeFactory: new ResolvedExporterTypeFactory(), + ); + } +}