From abbe1d90c2357df8236c7627d86a2c26ae802fb6 Mon Sep 17 00:00:00 2001 From: Fabio Capucci Date: Mon, 30 Dec 2024 13:38:59 +0100 Subject: [PATCH] dynamic render tag --- src/Render/RenderContext.php | 15 +++++++-- src/Tags/Custom/DynamicRenderTag.php | 17 ++++++++++ src/Tags/RenderTag.php | 33 ++++++++++++++++--- .../Tags/Custom/DynamicRenderTag.php | 20 +++++++++++ 4 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 src/Tags/Custom/DynamicRenderTag.php create mode 100644 tests/Integration/Tags/Custom/DynamicRenderTag.php diff --git a/src/Render/RenderContext.php b/src/Render/RenderContext.php index 5273321..1eb2c95 100644 --- a/src/Render/RenderContext.php +++ b/src/Render/RenderContext.php @@ -312,13 +312,24 @@ public function getTemplateName(): ?string return $this->templateName; } - public function loadPartial(string $templateName): Template + public function loadPartial(string $templateName, bool $parseIfMissing = false): Template { if ($partial = $this->sharedState->partialsCache->get($templateName)) { return $partial; } - throw new StandardException(sprintf("The partial '%s' has not be loaded during parsing", $templateName)); + if (! $parseIfMissing) { + throw new StandardException(sprintf("The partial '%s' has not be loaded during parsing", $templateName)); + } + + $parseContext = $this->environment->newParseContext(); + + $template = $parseContext->loadPartial($templateName); + + $this->sharedState->partialsCache->merge($parseContext->getPartialsCache()); + $this->sharedState->outputs->merge($parseContext->getOutputs()); + + return $template; } public function mergePartialsCache(PartialsCache $partialsCache): RenderContext diff --git a/src/Tags/Custom/DynamicRenderTag.php b/src/Tags/Custom/DynamicRenderTag.php new file mode 100644 index 0000000..afb9867 --- /dev/null +++ b/src/Tags/Custom/DynamicRenderTag.php @@ -0,0 +1,17 @@ +params->expression(); $this->templateNameExpression = match (true) { is_string($templateNameExpression) => $templateNameExpression, + $this->allowDynamicPartials() && $templateNameExpression instanceof VariableLookup => $templateNameExpression, default => throw new SyntaxException('Template name must be a string'), }; @@ -80,7 +81,9 @@ public function parse(TagParseContext $context): static $context->params->assertEnd(); - $context->getParseContext()->loadPartial($this->templateNameExpression); + if (is_string($this->templateNameExpression)) { + $context->getParseContext()->loadPartial($this->templateNameExpression); + } }); return $this; @@ -99,9 +102,10 @@ public function render(RenderContext $context): string public function stream(RenderContext $context): \Generator { - $partial = $context->loadPartial($this->templateNameExpression); + $partial = $this->loadPartial($context); + $templateName = $partial->name ?? ''; - $contextVariableName = $this->aliasName ?? Arr::last(explode('/', $this->templateNameExpression)); + $contextVariableName = $this->aliasName ?? Arr::last(explode('/', $templateName)); assert(is_string($contextVariableName)); $variable = $this->variableNameExpression ? $context->evaluate($this->variableNameExpression) : null; @@ -110,7 +114,7 @@ public function stream(RenderContext $context): \Generator $variable = $variable instanceof Traversable ? iterator_to_array($variable) : $variable; assert(is_array($variable)); - $forLoop = new ForLoopDrop($this->templateNameExpression, count($variable)); + $forLoop = new ForLoopDrop($templateName, count($variable)); foreach ($variable as $value) { $partialContext = $this->buildPartialContext($partial, $context, [ @@ -142,6 +146,20 @@ public function parseTreeVisitorChildren(): array ]; } + protected function loadPartial(RenderContext $context): Template + { + $templateName = $this->templateNameExpression; + if ($this->allowDynamicPartials() && $this->templateNameExpression instanceof VariableLookup) { + $templateName = $this->templateNameExpression->evaluate($context); + } + + if (! is_string($templateName)) { + throw new SyntaxException('Template name must be a string'); + } + + return $context->loadPartial($templateName, parseIfMissing: $this->allowDynamicPartials()); + } + protected function buildPartialContext(Template $partial, RenderContext $context, array $variables = []): RenderContext { $innerContext = $context->newIsolatedSubContext($partial->name); @@ -156,4 +174,9 @@ protected function buildPartialContext(Template $partial, RenderContext $context return $innerContext; } + + protected function allowDynamicPartials(): bool + { + return false; + } } diff --git a/tests/Integration/Tags/Custom/DynamicRenderTag.php b/tests/Integration/Tags/Custom/DynamicRenderTag.php new file mode 100644 index 0000000..e597e69 --- /dev/null +++ b/tests/Integration/Tags/Custom/DynamicRenderTag.php @@ -0,0 +1,20 @@ +setFilesystem(new StubFileSystem(partials: ['snippet' => 'echo'])) + ->setRethrowErrors(true) + ->build(); + + $environment->tagRegistry->register(\Keepsuit\Liquid\Tags\Custom\DynamicRenderTag::class); + + expect($environment->tagRegistry->get('render')) + ->toBe(\Keepsuit\Liquid\Tags\Custom\DynamicRenderTag::class); + + $template = $environment->parseString("{% assign name = 'snippet' %}{% render name %}"); + + expect($template->render($environment->newRenderContext()))->toBe('echo'); +});