Skip to content

Commit

Permalink
[Twig Hooks] Nullable HookableMetadata Property in `HookableLiveCom…
Browse files Browse the repository at this point in the history
…ponentTrait` (#10)

If we attempt to render a component that directly utilizes
`HookableLiveComponentTrait` without involving the `hooks` system, we
will encounter an error.

This component is directly rendered from the grid configuration:
<img width="701" alt="image"
src="https://github.com/Sylius/Stack/assets/40125720/5ab36df8-c116-454d-9dfc-5908fda7ba84">

We encountered with the following error:
<img width="1056" alt="image"
src="https://github.com/Sylius/Stack/assets/40125720/c619e648-d46b-43c3-b0f0-d75dad965c09">

Making the `HookableMetadata` property nullable allows to render the
component without any problems.
  • Loading branch information
GSadee authored May 7, 2024
2 parents a6a7732 + 5bcb4b8 commit f7ad292
Show file tree
Hide file tree
Showing 16 changed files with 163 additions and 4 deletions.
14 changes: 11 additions & 3 deletions src/TwigHooks/src/LiveComponent/HookableLiveComponentTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ trait HookableLiveComponentTrait
{
#[LiveProp(hydrateWith: 'hydrateHookableMetadata', dehydrateWith: 'dehydrateHookableMetadata')]
#[ExposeInTemplate('hookable_metadata')]
public HookableMetadata $hookableMetadata;
public ?HookableMetadata $hookableMetadata = null;

public function hydrateHookableMetadata($data): HookableMetadata
public function hydrateHookableMetadata($data): ?HookableMetadata
{
if (null === $data) {
return null;
}

return new HookableMetadata(
new HookMetadata($data['renderedBy'], new DataBag()),
new DataBag(),
Expand All @@ -27,8 +31,12 @@ public function hydrateHookableMetadata($data): HookableMetadata
);
}

public function dehydrateHookableMetadata(HookableMetadata $metadata): array
public function dehydrateHookableMetadata(?HookableMetadata $metadata = null): ?array
{
if (null === $metadata) {
return null;
}

return [
'renderedBy' => $metadata->renderedBy->name,
'configuration' => json_encode($metadata->configuration->all()),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
trait HookableComponentTrait
{
#[ExposeInTemplate('hookable_metadata')]
public HookableMetadata $hookableMetadata;
public ?HookableMetadata $hookableMetadata = null;
}
1 change: 1 addition & 0 deletions src/TwigHooks/src/Twig/HooksExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public function getFunctions(): array
new TwigFunction('get_hookable_metadata', [HooksRuntime::class, 'getHookableMetadata'], ['needs_context' => true]),
new TwigFunction('get_hookable_context', [HooksRuntime::class, 'getHookableContext'], ['needs_context' => true]),
new TwigFunction('get_hookable_configuration', [HooksRuntime::class, 'getHookableConfiguration'], ['needs_context' => true]),
new TwigFunction('is_hookable', [HooksRuntime::class, 'isHookable'], ['needs_context' => true]),
];
}

Expand Down
8 changes: 8 additions & 0 deletions src/TwigHooks/src/Twig/Runtime/HooksRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ public function getHookableConfiguration(array $context): ScalarDataBagInterface
return $this->getHookableMetadata($context)->configuration;
}

/**
* @param array<string, mixed> $context
*/
public function isHookable(array $context): bool
{
return array_key_exists(self::HOOKABLE_METADATA, $context) && $context[self::HOOKABLE_METADATA] instanceof HookableMetadata;
}

/**
* @param string|array<string> $hookNames
* @param array<string, mixed> $hookContext
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
Symfony\UX\TwigComponent\TwigComponentBundle::class => ['all' => true],
Symfony\UX\LiveComponent\LiveComponentBundle::class => ['all' => true],
Sylius\TwigHooks\TwigHooksBundle::class => ['all' => true],
];
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,11 @@ twig_hooks:
'hook_with_sections.index':
block:
template: 'hook_with_sections/index/block.html.twig'

'hookable_live_component':
dummy:
component: 'app:dummy_live'

'hookable_twig_component':
dummy:
component: 'app:dummy'
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
#index:
# path: /
# controller: App\Controller\DefaultController::index

live_component:
resource: '@LiveComponentBundle/config/routes.php'
prefix: '/_components'
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace TestApplication\Sylius\TwigHooks\Component;

use Sylius\TwigHooks\Twig\Component\HookableComponentTrait;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent('app:dummy', template: '_component/dummy.html.twig')]
final class DummyComponent
{
use DefaultActionTrait;
use HookableComponentTrait;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace TestApplication\Sylius\TwigHooks\Component;

use Sylius\TwigHooks\LiveComponent\HookableLiveComponentTrait;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent('app:dummy_live', template: '_component/dummy.html.twig')]
final class DummyLiveComponent
{
use DefaultActionTrait;
use HookableLiveComponentTrait;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% if is_hookable() %}
I'm a hookable!
{% else %}
I'm not a hookable :(
{% endif %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% hook 'hookable_live_component' %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<twig:app:dummy_live />
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{% hook 'hookable_twig_component' %}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<twig:app:dummy />
44 changes: 44 additions & 0 deletions src/TwigHooks/tests/Functional/Twig/HookableLiveComponentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Tests\Sylius\TwigHooks\Functional\Twig;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Twig\Environment as Twig;

/**
* @group kernel-required
*/
final class HookableLiveComponentTest extends KernelTestCase
{
public function testItRendersHookableLiveComponentWithUsingTwigHooks(): void
{
$this->assertSame(
<<<EXPECTED
<!-- BEGIN HOOK | name: "hookable_live_component" -->
<!-- BEGIN HOOKABLE | hook: "hookable_live_component", name: "dummy", component: "app:dummy_live", priority: 0 -->
I'm a hookable!
<!-- END HOOKABLE | hook: "hookable_live_component", name: "dummy", component: "app:dummy_live", priority: 0 -->
<!-- END HOOK | name: "hookable_live_component" -->
EXPECTED,
$this->render('hookable_live_component/hookable_component_rendered_with_using_twig_hooks.html.twig'),
);
}

public function testItRendersHookableLiveComponentWithoutUsingTwigHooks(): void
{
$this->assertStringContainsString(
"I'm not a hookable :(",
$this->render('hookable_live_component/hookable_component_rendered_without_using_twig_hooks.html.twig'),
);
}

private function render(string $path): string
{
/** @var Twig $twig */
$twig = $this->getContainer()->get('twig');

return $twig->render($path);
}
}
44 changes: 44 additions & 0 deletions src/TwigHooks/tests/Functional/Twig/HookableTwigComponentTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace Tests\Sylius\TwigHooks\Functional\Twig;

use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Twig\Environment as Twig;

/**
* @group kernel-required
*/
final class HookableTwigComponentTest extends KernelTestCase
{
public function testItRendersHookableLiveComponentWithUsingTwigHooks(): void
{
$this->assertSame(
<<<EXPECTED
<!-- BEGIN HOOK | name: "hookable_twig_component" -->
<!-- BEGIN HOOKABLE | hook: "hookable_twig_component", name: "dummy", component: "app:dummy", priority: 0 -->
I'm a hookable!
<!-- END HOOKABLE | hook: "hookable_twig_component", name: "dummy", component: "app:dummy", priority: 0 -->
<!-- END HOOK | name: "hookable_twig_component" -->
EXPECTED,
$this->render('hookable_twig_component/hookable_component_rendered_with_using_twig_hooks.html.twig'),
);
}

public function testItRendersHookableLiveComponentWithoutUsingTwigHooks(): void
{
$this->assertStringContainsString(
"I'm not a hookable :(",
$this->render('hookable_twig_component/hookable_component_rendered_without_using_twig_hooks.html.twig'),
);
}

private function render(string $path): string
{
/** @var Twig $twig */
$twig = $this->getContainer()->get('twig');

return $twig->render($path);
}
}

0 comments on commit f7ad292

Please sign in to comment.