Skip to content

Commit 7b0f600

Browse files
committed
Allow using directory name as component name for anonymous components
1 parent 1f7c8b9 commit 7b0f600

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)