Skip to content

Commit

Permalink
Merge pull request #1 from m-vo/feature/proxy-template
Browse files Browse the repository at this point in the history
use a template as proxy for rendering
  • Loading branch information
m-vo authored Mar 19, 2020
2 parents 98c22ec + 9235bbe commit 009c7a1
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 40 deletions.
42 changes: 26 additions & 16 deletions src/EventListener/RenderingForwarder.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@

class RenderingForwarder
{
private const TWIG_TEMPLATE = 'twig_template';
private const TEMPLATE_CONTEXT = 'context';

private Environment $twig;
private ContaoFramework $framework;
private string $rootDir;
Expand All @@ -29,9 +32,6 @@ class RenderingForwarder
/** @var array<string, string> */
private array $templates = [];

/** @var array<string, array> */
private array $templateContext = [];

public function __construct(Environment $twig, ContaoFramework $framework, string $rootDir, string $templateDir)
{
$this->twig = $twig;
Expand Down Expand Up @@ -68,30 +68,40 @@ public function registerTemplates(): void
}

/**
* @Hook("parseTemplate")
* @Hook("parseTemplate", priority=-128)
*/
public function storeTemplateContext(Template $template): void
public function delegateRendering(Template $contaoTemplate): void
{
$this->templateContext[$template->getName()] = $template->getData();
$template = $this->templates[$contaoTemplate->getName()] ?? null;

if (null === $template) {
return;
}

// delegate to our proxy template that will call render()
$contaoTemplate->setName('twig_template_proxy');

$contaoTemplate->setData([
self::TWIG_TEMPLATE => $template,
self::TEMPLATE_CONTEXT => $contaoTemplate->getData(),
]);
}

/**
* @Hook("parseFrontendTemplate")
*/
public function forwardRendering(string $buffer, string $identifier): string
public function render(Template $contaoTemplate): string
{
$context = $this->templateContext[$identifier] ?? null;
$template = $this->templates[$identifier] ?? null;
$data = $contaoTemplate->getData();

$template = $data[self::TWIG_TEMPLATE] ?? null;
$context = $data[self::TEMPLATE_CONTEXT] ?? null;

if (null === $context || null === $template) {
return $buffer;
if (null === $template) {
throw new \InvalidArgumentException("The template's context must contain a value for '".self::TWIG_TEMPLATE."'");
}

if (!$this->twig->getLoader()->exists($template)) {
throw new \RuntimeException("Template '$identifier' ($template) wasn't loaded.");
throw new \RuntimeException("Template '$template' wasn't loaded.");
}

// drop the current buffer and forward the rendering to twig instead
return $this->twig->render($template, $context);
}
}
8 changes: 8 additions & 0 deletions src/Resources/contao/templates/twig_template_proxy.html5
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

use Mvo\ContaoTwig\EventListener\RenderingForwarder;

// This template is just a proxy.
echo \System::getContainer()
->get(RenderingForwarder::class)
->render($this);
121 changes: 97 additions & 24 deletions tests/EventListener/RenderingForwarderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,30 +41,63 @@ public function testRegistersTemplates(): void
$renderingForwarder->registerTemplates();
}

public function testSkipsForwardRenderingIfTemplateNotRegistered(): void
public function testSetsProxyTemplate(): void
{
$renderingForwarder = $this->getRenderingForwarder(null, $this->mockContaoFramework());
$renderingForwarder = $this->getRenderingForwarder();

$renderingForwarder->setTemplatePaths($this->getTemplatePaths());
$renderingForwarder->registerTemplates();

/** @var Template&MockObject $template */
$template = $this->createMock(Template::class);
$template
->expects($this->once())
->method('getName')
->willReturn('bar');
$template
->expects($this->once())
->method('setName')
->with('twig_template_proxy');
$template
->expects($this->once())
->method('getData')
->willReturn(['a' => 123]);
$template
->expects($this->once())
->method('setData')
->with([
'twig_template' => 'sub/bar.html.twig',
'context' => ['a' => 123],
]);

$output = $renderingForwarder->forwardRendering('old buffer', 'text');
$this->assertSame('old buffer', $output);
$renderingForwarder->delegateRendering($template);
}

public function testSkipsForwardRenderingIfContextWasNotSet(): void
public function testSkipsSettingProxyTemplateForUnregisteredTemplates(): void
{
$renderingForwarder = $this->getRenderingForwarder();

$renderingForwarder->setTemplatePaths($this->getTemplatePaths());
$renderingForwarder->registerTemplates();

$output = $renderingForwarder->forwardRendering(
'old buffer',
'foo'
);
/** @var Template&MockObject $template */
$template = $this->createMock(Template::class);
$template
->expects($this->once())
->method('getName')
->willReturn('text');

$this->assertSame('old buffer', $output);
$template
->expects($this->never())
->method('setName');

$template
->expects($this->never())
->method('__set');

$renderingForwarder->delegateRendering($template);
}

public function testForwardRendering(): void
public function testRender(): void
{
$twigLoader = $this->createMock(LoaderInterface::class);
$twigLoader
Expand All @@ -85,30 +118,70 @@ public function testForwardRendering(): void
->method('getLoader')
->willReturn($twigLoader);

$renderingForwarder = $this->getRenderingForwarder($twig);

$renderingForwarder->setTemplatePaths($this->getTemplatePaths());
$renderingForwarder->registerTemplates();
$renderingForwarder = $this->getRenderingForwarder($twig, $this->mockContaoFramework());

/** @var Template&MockObject $template */
$template = $this->createMock(Template::class);
$template
->expects($this->once())
->method('getName')
->willReturn('bar');
->method('getData')
->willReturn([
'twig_template' => 'sub/bar.html.twig',
'context' => ['a' => 123],
]);

$output = $renderingForwarder->render($template);

$this->assertSame('twig content', $output);
}

public function testRenderThrowsIfTemplateNotSet(): void
{
$renderingForwarder = $this->getRenderingForwarder(null, $this->mockContaoFramework());

/** @var Template&MockObject $template */
$template = $this->createMock(Template::class);
$template
->expects($this->once())
->method('getData')
->willReturn(['a' => 123]);

$renderingForwarder->storeTemplateContext($template);
$this->expectExceptionMessage('The template\'s context must contain a value for \'twig_template\'');

$output = $renderingForwarder->forwardRendering(
'old buffer',
'bar'
);
$renderingForwarder->render($template);
}

$this->assertSame('twig content', $output);
public function testRenderThrowsIfTemplateWasNotLoaded(): void
{
$twigLoader = $this->createMock(LoaderInterface::class);
$twigLoader
->expects($this->once())
->method('exists')
->with('foobar.html.twig')
->willReturn(false);

/** @var Environment&MockObject $twig */
$twig = $this->createMock(Environment::class);
$twig
->expects($this->once())
->method('getLoader')
->willReturn($twigLoader);

$renderingForwarder = $this->getRenderingForwarder($twig, $this->mockContaoFramework());

/** @var Template&MockObject $template */
$template = $this->createMock(Template::class);
$template
->expects($this->once())
->method('getData')
->willReturn([
'twig_template' => 'foobar.html.twig',
'context' => ['a' => 123],
]);

$this->expectExceptionMessage('Template \'foobar.html.twig\' wasn\'t loaded.');

$renderingForwarder->render($template);
}

/**
Expand Down

0 comments on commit 009c7a1

Please sign in to comment.