Skip to content

Commit 3c6f465

Browse files
committed
feature symfony#58028 [TwigBridge] Render a block via the #[Template] attribute (smnandre)
This PR was squashed before being merged into the 7.2 branch. Discussion ---------- [TwigBridge] Render a `block` via the `#[Template]` attribute | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | no | New feature? | yes | Deprecations? | no | Issues | Fix #... | License | MIT The same way `renderBlock` was added in the [AbstractController](https://github.com/symfony/symfony/blob/7.2/src/Symfony/Bundle/FrameworkBundle/Controller/AbstractController.php#L259), and for the same reasons, this PR adds a `block` parameter on the Template attribute, used by the TemplateAttributeListener. --- Example taken from the [documentation](https://symfony.com/doc/current/templates.html#rendering-templates) ```php class ProductController extends AbstractController { #[Template('product/index.html.twig')] public function index(): array { // ... // when using the #[Template] attribute, you only need to return // an array with the parameters to pass to the template (the attribute // is the one which will create and return the Response object). return [ 'category' => '...', 'promotions' => ['...', '...'], ]; } } ``` With this PR ```php class ProductController extends AbstractController { #[Template('product/index.html.twig', block: 'main')] public function index(): array { return [ 'category' => '...', 'promotions' => ['...', '...'], ]; } } ``` --- 🛟 Only problem here, i don't get how to _test_ this properly.. `Twig\Template` is abstract, `Twig\TemplateWrapper` is final, and `Environment::load()` must return a `TemplateWrapper`.. I looked at the AbstractController tests but i don't find one covering the load->displayBlock / load->renderBlock. Commits ------- 14d4316 [TwigBridge] Render a `block` via the `#[Template]` attribute
2 parents b2e4eac + 14d4316 commit 3c6f465

File tree

3 files changed

+41
-2
lines changed

3 files changed

+41
-2
lines changed

src/Symfony/Bridge/Twig/Attribute/Template.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ class Template
2121
* @param string $template The name of the template to render
2222
* @param string[]|null $vars The controller method arguments to pass to the template
2323
* @param bool $stream Enables streaming the template
24+
* @param string|null $block The name of the block to use in the template
2425
*/
2526
public function __construct(
2627
public string $template,
2728
public ?array $vars = null,
2829
public bool $stream = false,
30+
public ?string $block = null,
2931
) {
3032
}
3133
}

src/Symfony/Bridge/Twig/EventListener/TemplateAttributeListener.php

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,16 @@ public function onKernelView(ViewEvent $event): void
5555
}
5656

5757
$event->setResponse($attribute->stream
58-
? new StreamedResponse(fn () => $this->twig->display($attribute->template, $parameters), $status)
59-
: new Response($this->twig->render($attribute->template, $parameters), $status)
58+
? new StreamedResponse(
59+
null !== $attribute->block
60+
? fn () => $this->twig->load($attribute->template)->displayBlock($attribute->block, $parameters)
61+
: fn () => $this->twig->display($attribute->template, $parameters),
62+
$status)
63+
: new Response(
64+
null !== $attribute->block
65+
? $this->twig->load($attribute->template)->renderBlock($attribute->block, $parameters)
66+
: $this->twig->render($attribute->template, $parameters),
67+
$status)
6068
);
6169
}
6270

src/Symfony/Bridge/Twig/Tests/EventListener/TemplateAttributeListenerTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
use Symfony\Bridge\Twig\Tests\Fixtures\TemplateAttributeController;
1818
use Symfony\Component\Form\FormInterface;
1919
use Symfony\Component\HttpFoundation\Request;
20+
use Symfony\Component\HttpFoundation\StreamedResponse;
2021
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
2122
use Symfony\Component\HttpKernel\Event\ViewEvent;
2223
use Symfony\Component\HttpKernel\HttpKernelInterface;
2324
use Twig\Environment;
25+
use Twig\Loader\ArrayLoader;
2426

2527
class TemplateAttributeListenerTest extends TestCase
2628
{
@@ -65,6 +67,33 @@ public function testAttribute()
6567
$this->assertSame('Bar', $event->getResponse()->getContent());
6668
}
6769

70+
public function testAttributeWithBlock()
71+
{
72+
$twig = new Environment(new ArrayLoader([
73+
'foo.html.twig' => 'ERROR {% block bar %}FOOBAR{% endblock %}',
74+
]));
75+
76+
$request = new Request();
77+
$kernel = $this->createMock(HttpKernelInterface::class);
78+
$controllerArgumentsEvent = new ControllerArgumentsEvent($kernel, [new TemplateAttributeController(), 'foo'], ['Bar'], $request, null);
79+
$listener = new TemplateAttributeListener($twig);
80+
81+
$request->attributes->set('_template', new Template('foo.html.twig', [], false, 'bar'));
82+
$event = new ViewEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, ['foo' => 'bar'], $controllerArgumentsEvent);
83+
$listener->onKernelView($event);
84+
$this->assertSame('FOOBAR', $event->getResponse()->getContent());
85+
86+
$request->attributes->set('_template', new Template('foo.html.twig', [], true, 'bar'));
87+
$event = new ViewEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, ['foo' => 'bar'], $controllerArgumentsEvent);
88+
$listener->onKernelView($event);
89+
$this->assertInstanceOf(StreamedResponse::class, $event->getResponse());
90+
91+
$request->attributes->set('_template', new Template('foo.html.twig', [], false, 'not_a_block'));
92+
$event = new ViewEvent($kernel, $request, HttpKernelInterface::MAIN_REQUEST, ['foo' => 'bar'], $controllerArgumentsEvent);
93+
$this->expectExceptionMessage('Block "not_a_block" on template "foo.html.twig" does not exist in "foo.html.twig".');
94+
$listener->onKernelView($event);
95+
}
96+
6897
public function testForm()
6998
{
7099
$request = new Request();

0 commit comments

Comments
 (0)