Skip to content

Commit c756147

Browse files
committed
feature #3169 [TwigComponent] Allow using directory name as component name for anonymous components (andyexeter)
This PR was merged into the 2.x branch. Discussion ---------- [TwigComponent] Allow using directory name as component name for anonymous components | Q | A | -------------- | --- | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- if yes, also update UPGRADE-*.md and src/**/CHANGELOG.md --> | Documentation? | yes <!-- required for new features, or documentation updates --> | Issues | Fix #1998 <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT This pull request introduces support for using `index.html.twig` as a component template when the component is placed in a directory named after itself. This helps avoid repetition and improves organization for nested components. The documentation is updated to explain this feature, and corresponding logic and tests are added to ensure correct template resolution. Commits ------- 7b0f600 Allow using directory name as component name for anonymous components
2 parents 1f7c8b9 + 7b0f600 commit c756147

File tree

9 files changed

+71
-0
lines changed

9 files changed

+71
-0
lines changed

src/TwigComponent/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Add option `profiler.collect_components` to control component data collection
66
in the profiler (enabled in debug mode by default)
7+
- Add support for using directory name as component name for anonymous components
78

89
## 2.30
910

src/TwigComponent/doc/index.rst

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,43 @@ the subdirectory:
676676
{# renders as: #}
677677
<button class="primary">Click Me!</button>
678678

679+
If your anonymous component lives in a directory with the same name, you can
680+
name the component file ``index.html.twig`` to avoid repetition:
681+
682+
.. code-block:: html+twig
683+
684+
{# templates/components/Menu/index.html.twig #}
685+
<nav {{ attributes.defaults({class: 'menu'}) }}>
686+
<ul>
687+
{% block content %}{% endblock %}
688+
</ul>
689+
</nav>
690+
691+
.. code-block:: html+twig
692+
693+
{# templates/components/Menu/Item.html.twig #}
694+
{% props href, label %}
695+
<li {{ attributes.defaults({class: 'menu__item'}) }}>
696+
<a href="{{ href }}">{% block content %}{{ label }}{% endblock %}</a>
697+
</li>
698+
699+
.. code-block:: html+twig
700+
701+
{# index.html.twig #}
702+
...
703+
<twig:Menu>
704+
<twig:Menu:Item href="/">Home</twig:Menu:Item>
705+
<twig:Menu:Item href="/about">About</twig:Menu:Item>
706+
</twig:Menu>
707+
708+
{# renders as: #}
709+
<nav class="menu">
710+
<ul>
711+
<li class="menu__item"><a href="/">Home</a></li>
712+
<li class="menu__item"><a href="/about">About</a></li>
713+
</ul>
714+
</nav>
715+
679716
Like normal, you can pass extra attributes that will be rendered on the element:
680717

681718
.. code-block:: html+twig

src/TwigComponent/src/ComponentTemplateFinder.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ public function findAnonymousComponentTemplate(string $name): ?string
6666
return $template;
6767
}
6868

69+
$template = rtrim($this->directory, '/').'/'.$componentPath.'/index.html.twig';
70+
if ($loader->exists($template)) {
71+
return $template;
72+
}
73+
6974
$parts = explode('/', $componentPath, 2);
7075
if (\count($parts) < 2) {
7176
return null;

src/TwigComponent/tests/Fixtures/templates/anonymous/Bar.html.twig

Whitespace-only changes.

src/TwigComponent/tests/Fixtures/templates/anonymous/Bar/index.html.twig

Whitespace-only changes.

src/TwigComponent/tests/Fixtures/templates/anonymous/Menu/Item.html.twig

Whitespace-only changes.

src/TwigComponent/tests/Fixtures/templates/anonymous/Menu/index.html.twig

Whitespace-only changes.

src/TwigComponent/tests/Integration/ComponentFactoryTest.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,29 @@ public function testLoadingAnonymousComponentFromBundle()
178178
$this->assertNull($metadata->get('class'));
179179
}
180180

181+
public function testLoadingAnonymousComponentWithFallback()
182+
{
183+
self::bootKernel(['environment' => 'anonymous_directory']);
184+
185+
$metadata = $this->factory()->metadataFor('Menu');
186+
187+
$this->assertSame('anonymous/Menu/index.html.twig', $metadata->getTemplate());
188+
$this->assertSame('Menu', $metadata->getName());
189+
$this->assertNull($metadata->get('class'));
190+
191+
$metadata = $this->factory()->metadataFor('Menu:Item');
192+
193+
$this->assertSame('anonymous/Menu/Item.html.twig', $metadata->getTemplate());
194+
$this->assertSame('Menu:Item', $metadata->getName());
195+
$this->assertNull($metadata->get('class'));
196+
197+
// Ensure anonymous/Bar.html.twig takes precedence over anonymous/Bar/index.html.twig
198+
$metadata = $this->factory()->metadataFor('Bar');
199+
$this->assertSame('anonymous/Bar.html.twig', $metadata->getTemplate());
200+
$this->assertSame('Bar', $metadata->getName());
201+
$this->assertNull($metadata->get('class'));
202+
}
203+
181204
public function testAutoNamingInSubDirectory()
182205
{
183206
$metadata = $this->factory()->metadataFor('SubDirectory:ComponentInSubDirectory');

src/TwigComponent/tests/Unit/ComponentTemplateFinderTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,18 @@ public function testFindTemplateWithinDirectory()
117117
'foo/bar.html.twig',
118118
'bar/foo/bar.html.twig',
119119
'foo/foo/bar.html.twig',
120+
'foo/qux/index.html.twig',
121+
'foo/foo/baz/index.html.twig',
120122
];
121123
$loader = $this->createLoader($templates);
122124
$finder = new ComponentTemplateFinder($loader, 'foo');
123125

124126
$this->assertEquals('foo/bar.html.twig', $finder->findAnonymousComponentTemplate('bar'));
125127
$this->assertEquals('foo/foo/bar.html.twig', $finder->findAnonymousComponentTemplate('foo:bar'));
126128
$this->assertEquals('foo/foo/bar.html.twig', $finder->findAnonymousComponentTemplate('foo:bar'));
129+
130+
$this->assertEquals('foo/qux/index.html.twig', $finder->findAnonymousComponentTemplate('qux'));
131+
$this->assertEquals('foo/foo/baz/index.html.twig', $finder->findAnonymousComponentTemplate('foo:baz'));
127132
}
128133

129134
private function createLoader(array $templates): LoaderInterface

0 commit comments

Comments
 (0)