Skip to content

Commit

Permalink
Add Component Template Finder
Browse files Browse the repository at this point in the history
  • Loading branch information
loic425 committed Jun 26, 2024
1 parent 4588d89 commit b847083
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 0 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
},
"autoload": {
"psr-4": {
"Sylius\\TwigComponents\\": "src/TwigComponents/src/",
"Sylius\\TwigExtra\\": "src/TwigExtra/src/",
"Sylius\\TwigHooks\\": "src/TwigHooks/src/"
}
Expand Down
1 change: 1 addition & 0 deletions config/bundles.php
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
4 changes: 4 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
</php>

<testsuites>
<testsuite name="TwigComponents Test Suite">
<directory>src/TwigComponents/tests</directory>
</testsuite>

<testsuite name="TwigExtra Test Suite">
<directory>src/TwigExtra/tests</directory>
</testsuite>
Expand Down
9 changes: 9 additions & 0 deletions src/TwigExtra/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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'),
])
;
};
42 changes: 42 additions & 0 deletions src/TwigExtra/src/Symfony/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace Sylius\TwigExtra\Symfony\DependencyInjection;

use Sylius\TwigHooks\Hookable\DisabledHookable;
use Sylius\TwigHooks\Hookable\HookableComponent;
use Sylius\TwigHooks\Hookable\HookableTemplate;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;

final class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder(): TreeBuilder
{
$treeBuilder = new TreeBuilder('sylius_twig_extra');

$rootNode = $treeBuilder->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()
;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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'] ?? []);
}
}
47 changes: 47 additions & 0 deletions src/TwigExtra/src/Twig/Ux/ComponentTemplateFinder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace Sylius\TwigExtra\Twig\Ux;

use Symfony\UX\TwigComponent\ComponentTemplateFinderInterface;
use Twig\Loader\LoaderInterface;

final class ComponentTemplateFinder implements ComponentTemplateFinderInterface
{
/** @param array<string, string> $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));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace Tests\Sylius\TwigExtra\Integration\DependencyInjection;

use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
use Sylius\TwigExtra\Symfony\DependencyInjection\TwigExtraExtension;
use Sylius\TwigExtra\Twig\Ux\ComponentTemplateFinder;

final class TwigExtraExtensionTest extends AbstractExtensionTestCase
{
public function testItRegistersTwigUxComponentTemplateFinder(): void
{
$this->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(),
];
}
}
57 changes: 57 additions & 0 deletions src/TwigExtra/tests/Unit/Twig/Ux/ComponentTemplateFinderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace Tests\Sylius\TwigExtra\Unit\Twig\Ux;

use PHPUnit\Framework\TestCase;
use Sylius\TwigExtra\Twig\Ux\ComponentTemplateFinder;
use Symfony\UX\TwigComponent\ComponentTemplateFinder as DecoratedComponentTemplateFinder;
use Twig\Loader\ArrayLoader;
use Twig\Loader\LoaderInterface;

final class ComponentTemplateFinderTest extends TestCase
{
public function testItCallsDecoratedFinderIfNoPrefixMatches(): void
{
$componentTemplateFinder = $this->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);
}
}

0 comments on commit b847083

Please sign in to comment.