diff --git a/composer.json b/composer.json index db415479..573a3530 100644 --- a/composer.json +++ b/composer.json @@ -46,6 +46,7 @@ }, "autoload": { "psr-4": { + "Sylius\\TwigComponents\\": "src/TwigComponents/src/", "Sylius\\TwigExtra\\": "src/TwigExtra/src/", "Sylius\\TwigHooks\\": "src/TwigHooks/src/" } diff --git a/config/bundles.php b/config/bundles.php index b7364282..e73cab19 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -4,6 +4,7 @@ Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true], Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true], Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true], + Sylius\TwigComponents\Symfony\TwigComponentsBundle::class => ['all' => true], Sylius\TwigHooks\TwigHooksBundle::class => ['all' => true], Sylius\TwigExtra\Symfony\TwigExtraBundle::class => ['all' => true], Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true], diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 7e9b83d4..b85fb730 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -18,6 +18,10 @@ + + src/TwigComponents/tests + + src/TwigExtra/tests diff --git a/src/TwigExtra/config/services.php b/src/TwigExtra/config/services.php index 90b3395b..e8c2caa4 100644 --- a/src/TwigExtra/config/services.php +++ b/src/TwigExtra/config/services.php @@ -7,6 +7,7 @@ use Sylius\TwigExtra\Twig\Extension\SortByExtension; use Sylius\TwigExtra\Twig\Extension\TestFormAttributeExtension; use Sylius\TwigExtra\Twig\Extension\TestHtmlAttributeExtension; +use Sylius\TwigExtra\Twig\Ux\ComponentTemplateFinder; return function (ContainerConfigurator $configurator): void { $services = $configurator->services(); @@ -30,4 +31,12 @@ ]) ->tag(name: 'twig.extension') ; + + $services->set('sylius_twig_extra.twig.ux.component_template_finder', ComponentTemplateFinder::class) + ->args([ + service('.inner'), + service('twig.loader.native_filesystem'), + param('sylius_twig_extra.twig_ux.anonymous_component_template_prefixes'), + ]) + ; }; diff --git a/src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php b/src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php new file mode 100644 index 00000000..e680d72e --- /dev/null +++ b/src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php @@ -0,0 +1,42 @@ +getRootNode(); + + $this->addTwigUxConfiguration($rootNode); + + return $treeBuilder; + } + + private function addTwigUxConfiguration(ArrayNodeDefinition $rootNode): void + { + $rootNode + ->children() + ->arrayNode('twig_ux') + ->children() + ->arrayNode('anonymous_component_template_prefixes') + ->useAttributeAsKey('prefix_name') + ->scalarPrototype()->end() + ->end() + ->end() + ->end() + ->end() + ; + } +} diff --git a/src/TwigExtra/src/Symfony/DependencyInjection/TwigExtraExtension.php b/src/TwigExtra/src/Symfony/DependencyInjection/TwigExtraExtension.php index 33427009..304183f3 100644 --- a/src/TwigExtra/src/Symfony/DependencyInjection/TwigExtraExtension.php +++ b/src/TwigExtra/src/Symfony/DependencyInjection/TwigExtraExtension.php @@ -19,5 +19,10 @@ public function load(array $configs, ContainerBuilder $container): void ); $loader->load('services.php'); + + $configuration = $this->getConfiguration([], $container); + $config = $this->processConfiguration($configuration, $configs); + + $container->setParameter('sylius_twig_extra.twig_ux.anonymous_component_template_prefixes', $config['twig_ux']['anonymous_component_template_prefixes'] ?? []); } } diff --git a/src/TwigExtra/src/Twig/Ux/ComponentTemplateFinder.php b/src/TwigExtra/src/Twig/Ux/ComponentTemplateFinder.php new file mode 100644 index 00000000..c66be86b --- /dev/null +++ b/src/TwigExtra/src/Twig/Ux/ComponentTemplateFinder.php @@ -0,0 +1,47 @@ + $anonymousComponentTemplatePrefixes */ + public function __construct( + private readonly ComponentTemplateFinderInterface $decorated, + private readonly LoaderInterface $loader, + private readonly array $anonymousComponentTemplatePrefixes, + ) { + } + + public function findAnonymousComponentTemplate(string $name): ?string + { + foreach ($this->anonymousComponentTemplatePrefixes as $prefixName => $prefixTemplatePath) { + $prefixName = sprintf('%s:', $prefixName); + if (str_starts_with($name, $prefixName)) { + return $this->getTemplatePath($name, $prefixName, $prefixTemplatePath); + } + } + + return $this->decorated->findAnonymousComponentTemplate($name); + } + + private function getTemplatePath(string $name, string $prefixName, string $prefixTemplatePath): ?string + { + $templatePath = sprintf('%s/%s.html.twig', $prefixTemplatePath, $this->normalizeName($name, $prefixName)); + + if ($this->loader->exists($templatePath)) { + return $templatePath; + } + + return null; + } + + private function normalizeName(string $name, string $prefixName): string + { + return str_replace(':', '/', str_replace($prefixName, '', $name)); + } +} diff --git a/src/TwigExtra/tests/Integration/DependencyInjection/TwigExtraExtensionTest.php b/src/TwigExtra/tests/Integration/DependencyInjection/TwigExtraExtensionTest.php new file mode 100644 index 00000000..5caafa51 --- /dev/null +++ b/src/TwigExtra/tests/Integration/DependencyInjection/TwigExtraExtensionTest.php @@ -0,0 +1,41 @@ +load(); + + $this->assertContainerBuilderHasService('sylius_twig_extra.twig.ux.component_template_finder', ComponentTemplateFinder::class); + } + + public function testItRegistersTwigUxAnonymousComponentTemplatePrefixesParameter(): void + { + $this->load([ + 'twig_ux' => [ + 'anonymous_component_template_prefixes' => [ + 'sylius_admin_ui:component' => '@SyliusAdminUi/components', + ], + ], + ]); + + $this->assertContainerBuilderHasParameter('sylius_twig_extra.twig_ux.anonymous_component_template_prefixes', [ + 'sylius_admin_ui:component' => '@SyliusAdminUi/components', + ]); + } + + protected function getContainerExtensions(): array + { + return [ + new TwigExtraExtension(), + ]; + } +} diff --git a/src/TwigExtra/tests/Unit/Twig/Ux/ComponentTemplateFinderTest.php b/src/TwigExtra/tests/Unit/Twig/Ux/ComponentTemplateFinderTest.php new file mode 100644 index 00000000..8e337d3d --- /dev/null +++ b/src/TwigExtra/tests/Unit/Twig/Ux/ComponentTemplateFinderTest.php @@ -0,0 +1,57 @@ +createTemplateFinder( + ['components/sylius_admin_ui/component.html.twig' => ''], + [], + ); + + $this->assertEquals( + 'components/sylius_admin_ui/component.html.twig', + $componentTemplateFinder->findAnonymousComponentTemplate('sylius_admin_ui:component'), + ); + } + + public function testItFindsAnonymousComponentTemplate(): void + { + $componentTemplateFinder = $this->createTemplateFinder( + ['@SyliusBootstrapUi/components/some_sub_component.html.twig' => ''], + ['sylius_admin_ui:component' => '@SyliusBootstrapUi/components'], + ); + + $this->assertEquals( + '@SyliusBootstrapUi/components/some_sub_component.html.twig', + $componentTemplateFinder->findAnonymousComponentTemplate('sylius_admin_ui:component:some_sub_component'), + ); + } + + private function createTemplateFinder(array $templates, array $anonymousComponentTemplatePrefixes): ComponentTemplateFinder + { + return new ComponentTemplateFinder( + new DecoratedComponentTemplateFinder( + $this->createLoader($templates), + 'components', + ), + $this->createLoader($templates), + $anonymousComponentTemplatePrefixes, + ); + } + + private function createLoader(array $templates): LoaderInterface + { + return new ArrayLoader($templates); + } +}