Skip to content

Commit

Permalink
[TwigHooks] Template configuration provider with context (#208)
Browse files Browse the repository at this point in the history
Fixes #11

```yaml
sylius_twig_hooks:
    hooks:
        'sylius_admin.book.show.content.header.title_block':
            title:
                template: '@SyliusBootstrapAdminUi/shared/crud/common/content/header/title_block/title.html.twig'
                configuration:
                    title: '@=_context.book.getTitle()'
```
  • Loading branch information
loic425 authored Jan 8, 2025
2 parents 36039fe + 821aba3 commit d34a35b
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 15 deletions.
6 changes: 6 additions & 0 deletions config/packages/sylius_twig_hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ sylius_twig_hooks:
description:
template: 'book/index/content/header/description.html.twig'

'sylius_admin.book.show.content.header.title_block':
title:
template: '@SyliusBootstrapAdminUi/shared/crud/common/content/header/title_block/title.html.twig'
configuration:
title: '@=_context.book.getTitle()'

'sylius_admin.talk.create.content':
form:
component: 'App\Twig\Component\TalkFormComponent'
Expand Down
11 changes: 11 additions & 0 deletions src/TwigHooks/config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
use Sylius\TwigHooks\Provider\ComponentPropsProvider;
use Sylius\TwigHooks\Provider\DefaultConfigurationProvider;
use Sylius\TwigHooks\Provider\DefaultContextProvider;
use Sylius\TwigHooks\Provider\PropsProviderInterface;
use Sylius\TwigHooks\Provider\TemplateConfigurationProvider;
use Sylius\TwigHooks\Provider\TemplateConfigurationProviderInterface;
use Sylius\TwigHooks\Registry\HookablesRegistry;
use Sylius\TwigHooks\Twig\HooksExtension;
use Sylius\TwigHooks\Twig\Runtime\HooksRuntime;
Expand All @@ -39,9 +42,17 @@
inline_service(ExpressionLanguage::class),
])
;
$services->alias(PropsProviderInterface::class, 'sylius_twig_hooks.provider.component_props');

$services->set('sylius_twig_hooks.provider.default_configuration', DefaultConfigurationProvider::class);

$services->set('sylius_twig_hooks.provider.template_configuration', TemplateConfigurationProvider::class)
->args([
inline_service(ExpressionLanguage::class),
])
;
$services->alias(TemplateConfigurationProviderInterface::class, 'sylius_twig_hooks.provider.template_configuration');

$services->set('sylius_twig_hooks.registry.hookables', HookablesRegistry::class)
->args([
tagged_iterator('sylius_twig_hooks.hookable'),
Expand Down
1 change: 1 addition & 0 deletions src/TwigHooks/config/services/hookable_renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
$services->set('sylius_twig_hooks.renderer.hookable.template', HookableTemplateRenderer::class)
->args([
service('twig'),
service('sylius_twig_hooks.provider.template_configuration'),
])
->tag('sylius_twig_hooks.hookable_renderer')
;
Expand Down
5 changes: 5 additions & 0 deletions src/TwigHooks/src/Hookable/Metadata/HookableMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,9 @@ public function hasPrefixes(): bool
{
return count($this->prefixes) > 0;
}

public function withConfiguration(ScalarDataBagInterface $configuration): self
{
return new self($this->renderedBy, $this->context, $configuration, $this->prefixes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@

namespace Sylius\TwigHooks\Hookable\Renderer;

use Sylius\TwigHooks\Bag\ScalarDataBag;
use Sylius\TwigHooks\Hookable\AbstractHookable;
use Sylius\TwigHooks\Hookable\HookableTemplate;
use Sylius\TwigHooks\Hookable\Metadata\HookableMetadata;
use Sylius\TwigHooks\Hookable\Renderer\Exception\HookRenderException;
use Sylius\TwigHooks\Provider\TemplateConfigurationProviderInterface;
use Sylius\TwigHooks\Twig\Runtime\HooksRuntime;
use Twig\Environment as Twig;

final class HookableTemplateRenderer implements SupportableHookableRendererInterface
{
public function __construct(
private readonly Twig $twig,
private readonly TemplateConfigurationProviderInterface $configurationProvider,
) {
}

Expand All @@ -39,6 +42,9 @@ public function render(AbstractHookable $hookable, HookableMetadata $metadata):
}

try {
$configuration = $this->configurationProvider->provide($hookable, $metadata);
$metadata = $metadata->withConfiguration(new ScalarDataBag($configuration));

return $this->twig->render($hookable->template, [
HooksRuntime::HOOKABLE_METADATA => $metadata,
]);
Expand Down
72 changes: 72 additions & 0 deletions src/TwigHooks/src/Provider/TemplateConfigurationProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\TwigHooks\Provider;

use Sylius\TwigHooks\Hookable\HookableTemplate;
use Sylius\TwigHooks\Hookable\Metadata\HookableMetadata;
use Sylius\TwigHooks\Provider\Exception\InvalidExpressionException;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;

final class TemplateConfigurationProvider implements TemplateConfigurationProviderInterface
{
public function __construct(
private readonly ExpressionLanguage $expressionLanguage,
) {
}

public function provide(HookableTemplate $hookable, HookableMetadata $metadata): array
{
$values = [
'_context' => $metadata->context,
];

return $this->mapArrayRecursively(function (mixed $value) use ($values, $hookable): mixed {
if (is_string($value) && str_starts_with($value, '@=')) {
try {
return $this->expressionLanguage->evaluate(substr($value, 2), $values);
} catch (\Throwable $e) {
throw new InvalidExpressionException(
sprintf(
'Failed to evaluate the "%s" expression while rendering the "%s" hookable in the "%s" hook. Error: %s".',
$value,
$hookable->name,
$hookable->hookName,
$e->getMessage(),
),
previous: $e,
);
}
}

return $value;
}, $hookable->configuration);
}

/**
* @param array<array-key, mixed> $array
*
* @return array<array-key, mixed>
*/
private function mapArrayRecursively(callable $callback, array $array): array
{
$result = [];
foreach ($array as $key => $value) {
$result[$key] = is_array($value)
? $this->mapArrayRecursively($callback, $value)
: $callback($value);
}

return $result;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\TwigHooks\Provider;

use Sylius\TwigHooks\Hookable\HookableTemplate;
use Sylius\TwigHooks\Hookable\Metadata\HookableMetadata;
use Sylius\TwigHooks\Provider\Exception\InvalidExpressionException;

interface TemplateConfigurationProviderInterface
{
/**
* @throws InvalidExpressionException
*
* @return array<string, mixed>
*/
public function provide(HookableTemplate $hookable, HookableMetadata $metadata): array;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@
use Sylius\TwigHooks\Hookable\Metadata\HookableMetadata;
use Sylius\TwigHooks\Hookable\Renderer\Exception\HookRenderException;
use Sylius\TwigHooks\Hookable\Renderer\HookableTemplateRenderer;
use Sylius\TwigHooks\Provider\TemplateConfigurationProvider;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Tests\Sylius\TwigHooks\Utils\MotherObject\HookableComponentMotherObject;
use Tests\Sylius\TwigHooks\Utils\MotherObject\HookableMetadataMotherObject;
use Tests\Sylius\TwigHooks\Utils\MotherObject\HookableTemplateMotherObject;
use Twig\Environment as Twig;
use Twig\Error\Error;
Expand Down Expand Up @@ -55,7 +58,7 @@ public function testItThrowsAnExceptionWhenTryingToRenderUnsupportedHookable():

public function testItRendersHookableTemplate(): void
{
$metadata = $this->createMock(HookableMetadata::class);
$metadata = HookableMetadataMotherObject::some();

$this->twig->expects($this->once())->method('render')->with('some-template', [
'hookable_metadata' => $metadata,
Expand All @@ -69,7 +72,7 @@ public function testItRendersHookableTemplate(): void

public function testItThrowsAnExceptionWhenTwigThrowsAnError(): void
{
$metadata = $this->createMock(HookableMetadata::class);
$metadata = HookableMetadataMotherObject::some();

$this->twig->expects($this->once())->method('render')->with('some-template', [
'hookable_metadata' => $metadata,
Expand All @@ -78,13 +81,13 @@ public function testItThrowsAnExceptionWhenTwigThrowsAnError(): void
$hookable = HookableTemplateMotherObject::withTarget('some-template');

$this->expectException(HookRenderException::class);
$this->expectExceptionMessage('An error occurred during rendering the "some_name" hook in the "some_hook" hookable. Unable to find the template at line 76.');
$this->expectExceptionMessage('An error occurred during rendering the "some_name" hook in the "some_hook" hookable. Unable to find the template at line 79.');

$this->getTestSubject()->render($hookable, $metadata);
}

private function getTestSubject(): HookableTemplateRenderer
{
return new HookableTemplateRenderer($this->twig);
return new HookableTemplateRenderer($this->twig, new TemplateConfigurationProvider(new ExpressionLanguage()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Tests\Sylius\TwigHooks\Unit\Provider;

use PHPUnit\Framework\TestCase;
use Sylius\TwigHooks\Provider\TemplateConfigurationProvider;
use Sylius\TwigHooks\Provider\TemplateConfigurationProviderInterface;
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
use Tests\Sylius\TwigHooks\Utils\MotherObject\HookableMetadataMotherObject;
use Tests\Sylius\TwigHooks\Utils\MotherObject\HookableTemplateMotherObject;

final class TemplatesConfigurationProviderTest extends TestCase
{
public function testItReturnsEmptyArrayWhenNoConfigurationDefined(): void
{
$hookable = HookableTemplateMotherObject::withConfiguration([]);
$metadata = HookableMetadataMotherObject::some();

$templateConfigurationProvider = $this->createTestSubject();

$this->assertSame([], $templateConfigurationProvider->provide($hookable, $metadata));
}

public function testItReturnsConfiguration(): void
{
$hookable = HookableTemplateMotherObject::withConfiguration(['message' => 'Hello, World!']);
$metadata = HookableMetadataMotherObject::some();

$templateConfigurationProvider = $this->createTestSubject();

$this->assertSame(['message' => 'Hello, World!'], $templateConfigurationProvider->provide($hookable, $metadata));
}

public function testItEvaluatesExpressions(): void
{
$hookable = HookableTemplateMotherObject::withConfiguration([
'username' => '@=_context.username',
]);
$metadata = HookableMetadataMotherObject::withContext(
['username' => 'Jacob'],
);

$templateConfigurationProvider = $this->createTestSubject();

$this->assertSame(['username' => 'Jacob'], $templateConfigurationProvider->provide($hookable, $metadata));
}

private function createTestSubject(): TemplateConfigurationProviderInterface
{
return new TemplateConfigurationProvider(new ExpressionLanguage());
}
}
7 changes: 0 additions & 7 deletions templates/book/show/content/page_body.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,10 @@
<div class="page-body">
<div class="container-xl">
<div class="card mb-3">
<div class="card-header">
<div class="card-title" {{ sylius_test_html_attribute('title') }}>
{{ book.title }}
</div>
</div>
<div class="card-body" {{ sylius_test_html_attribute('author-name') }}>
<strong>{{ 'app.ui.author'|trans }}:</strong>
{{ book.authorName }}
</div>
</div>
</div>
</div>


3 changes: 1 addition & 2 deletions tests/Functional/BookTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ public function testShowingBook(): void
self::assertResponseIsSuccessful();

// Validate Header
self::assertSelectorTextContains('h1.page-title', 'Show Book');
self::assertSelectorTextContains('h1.page-title', 'Shinning');

// Validate page body
self::assertSelectorTextContains('[data-test-title]', 'Shinning');
self::assertSelectorTextContains('[data-test-author-name]', 'Stephen King');
}

Expand Down
4 changes: 2 additions & 2 deletions tests/Translations/FrenchTranslatedUiTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ public function testShowItem(): void

self::assertResponseIsSuccessful();

// Validate Header
self::assertSelectorTextContains('h1.page-title', 'Afficher Livre');
// Validate Body
self::assertSelectorTextContains('[data-test-author-name] strong', 'Auteur');
}

public function testBrowsingItems(): void
Expand Down

0 comments on commit d34a35b

Please sign in to comment.