Skip to content

Commit 8ce6621

Browse files
committed
[TwigComponent][LiveComponent] refactor component config
1 parent 0f5b21e commit 8ce6621

File tree

10 files changed

+53
-45
lines changed

10 files changed

+53
-45
lines changed

src/LiveComponent/src/EventListener/LiveComponentSubscriber.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function onKernelRequest(RequestEvent $event): void
7878
throw new NotFoundHttpException(sprintf('Component "%s" not found.', $componentName), $e);
7979
}
8080

81-
$request->attributes->set('_component_template', $config['template']);
81+
$request->attributes->set('_component_config', $config);
8282

8383
if ('get' === $action) {
8484
$defaultAction = trim($config['default_action'] ?? '__invoke', '()');
@@ -232,7 +232,7 @@ private function createResponse(object $component, Request $request): Response
232232

233233
$html = $this->container->get(ComponentRenderer::class)->render(
234234
$component,
235-
$request->attributes->get('_component_template')
235+
$request->attributes->get('_component_config')
236236
);
237237

238238
return new Response($html);

src/LiveComponent/src/Resources/doc/index.rst

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ A real-time product search component might look like this::
4646
.. code-block:: twig
4747
4848
{# templates/components/product_search.html.twig #}
49-
<div {{ init_live_component(this) }}>
49+
<div {{ init_live_component() }}>
5050
<input
5151
type="search"
5252
name="query"
@@ -165,7 +165,7 @@ initialize the Stimulus controller:
165165
.. code-block:: diff
166166
167167
- <div>
168-
+ <div {{ init_live_component(this) }}>
168+
+ <div {{ init_live_component() }}>
169169
<strong>{{ this.randomNumber }}</strong>
170170
</div>
171171
@@ -176,7 +176,7 @@ and give the user a new random number:
176176

177177
.. code-block:: twig
178178
179-
<div {{ init_live_component(this) }}>
179+
<div {{ init_live_component() }}>
180180
<strong>{{ this.randomNumber }}</strong>
181181
182182
<button
@@ -251,7 +251,7 @@ Let's add two inputs to our template:
251251
.. code-block:: twig
252252
253253
{# templates/components/random_number.html.twig #}
254-
<div {{ init_live_component(this) }}>
254+
<div {{ init_live_component() }}>
255255
<input
256256
type="number"
257257
value="{{ min }}"
@@ -368,7 +368,7 @@ property. The following code works identically to the previous example:
368368

369369
.. code-block:: diff
370370
371-
<div {{ init_live_component(this)>
371+
<div {{ init_live_component()>
372372
<input
373373
type="number"
374374
value="{{ min }}"
@@ -791,7 +791,7 @@ as ``this.form`` thanks to the trait:
791791
792792
{# templates/components/post_form.html.twig #}
793793
<div
794-
{{ init_live_component(this) }}
794+
{{ init_live_component() }}
795795
{#
796796
Automatically catch all "change" events from the fields
797797
below and re-render the component.
@@ -815,7 +815,7 @@ as ``this.form`` thanks to the trait:
815815
</div>
816816
817817
Mostly, this is a pretty boring template! It includes the normal
818-
``init_live_component(this)`` and then you render the form however you
818+
``init_live_component()`` and then you render the form however you
819819
want.
820820

821821
But the result is incredible! As you finish changing each field, the
@@ -1024,7 +1024,7 @@ section above) is to add:
10241024
.. code-block:: diff
10251025
10261026
<div
1027-
{{ init_live_component(this) }}
1027+
{{ init_live_component() }}
10281028
+ data-action="change->live#update"
10291029
>
10301030
@@ -1056,7 +1056,7 @@ rendered the ``content`` through a Markdown filter from the
10561056

10571057
.. code-block:: twig
10581058
1059-
<div {{init_live_component(this)}}>
1059+
<div {{init_live_component()}}>
10601060
<input
10611061
type="text"
10621062
value="{{ post.title }}"
@@ -1221,7 +1221,7 @@ You can also use “polling” to continually refresh a component. On the
12211221
.. code-block:: diff
12221222
12231223
<div
1224-
{{ init_live_component(this) }}
1224+
{{ init_live_component() }}
12251225
+ data-poll
12261226
>
12271227
@@ -1233,7 +1233,7 @@ delay for 500ms:
12331233
.. code-block:: twig
12341234
12351235
<div
1236-
{{ init_live_component(this) }}
1236+
{{ init_live_component() }}
12371237
data-poll="delay(500)|$render"
12381238
>
12391239
@@ -1242,7 +1242,7 @@ You can also trigger a specific “action” instead of a normal re-render:
12421242
.. code-block:: twig
12431243
12441244
<div
1245-
{{ init_live_component(this) }}
1245+
{{ init_live_component() }}
12461246
12471247
data-poll="save"
12481248
{#
@@ -1437,7 +1437,7 @@ In the ``EditPostComponent`` template, you render the
14371437
.. code-block:: twig
14381438
14391439
{# templates/components/edit_post.html.twig #}
1440-
<div {{ init_live_component(this) }}>
1440+
<div {{ init_live_component() }}>
14411441
<input
14421442
type="text"
14431443
name="post[title]"
@@ -1459,7 +1459,7 @@ In the ``EditPostComponent`` template, you render the
14591459
14601460
.. code-block:: twig
14611461
1462-
<div {{ init_live_component(this) }} class="mb-3">
1462+
<div {{ init_live_component() }} class="mb-3">
14631463
<textarea
14641464
name="{{ name }}"
14651465
data-model="value"

src/LiveComponent/src/Twig/LiveComponentExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ final class LiveComponentExtension extends AbstractExtension
2424
public function getFunctions(): array
2525
{
2626
return [
27-
new TwigFunction('init_live_component', [LiveComponentRuntime::class, 'renderLiveAttributes'], ['needs_environment' => true, 'is_safe' => ['html_attr']]),
27+
new TwigFunction('init_live_component', [LiveComponentRuntime::class, 'renderLiveAttributes'], ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['html_attr']]),
2828
new TwigFunction('component_url', [LiveComponentRuntime::class, 'getComponentUrl']),
2929
];
3030
}

src/LiveComponent/src/Twig/LiveComponentRuntime.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,15 @@ public function __construct(LiveComponentHydrator $hydrator, ComponentFactory $f
3737
$this->csrfTokenManager = $csrfTokenManager;
3838
}
3939

40-
public function renderLiveAttributes(Environment $env, object $component, string $name = null): string
40+
public function renderLiveAttributes(Environment $env, array $context): string
4141
{
42-
$name = $this->nameFor($component, $name);
42+
if (!isset($context['_component_config'])) {
43+
throw new \LogicException('init_live_component can only be called within a component template.');
44+
}
45+
46+
$name = $context['_component_config']['name'];
4347
$url = $this->urlGenerator->generate('live_component', ['component' => $name]);
44-
$data = $this->hydrator->dehydrate($component);
48+
$data = $this->hydrator->dehydrate($context['this']);
4549

4650
$ret = sprintf(
4751
'data-controller="live" data-live-url-value="%s" data-live-data-value="%s"',
@@ -66,9 +70,4 @@ public function getComponentUrl(string $name, array $props = []): string
6670

6771
return $this->urlGenerator->generate('live_component', $params);
6872
}
69-
70-
private function nameFor(object $component, string $name = null): string
71-
{
72-
return $this->factory->configFor($component, $name)['name'];
73-
}
7473
}

src/LiveComponent/tests/Fixture/templates/components/component1.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div
2-
{{ init_live_component(this) }}
2+
{{ init_live_component() }}
33
>
44
Prop1: {{ this.prop1.id }}
55
Prop2: {{ this.prop2|date('Y-m-d g:i') }}

src/LiveComponent/tests/Fixture/templates/components/component2.html.twig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<div
2-
{{ init_live_component(this) }}
2+
{{ init_live_component() }}
33
>
44
Count: {{ this.count }}
55
BeforeReRenderCalled: {{ this.beforeReRenderCalled ? 'Yes' : 'No' }}

src/TwigComponent/src/ComponentFactory.php

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,38 +36,34 @@ public function __construct(ServiceLocator $components, PropertyAccessorInterfac
3636
}
3737

3838
/**
39-
* @param string|object $component Component name as string or component object
39+
* @param string|object $name Component name, component object or component FQCN
4040
*/
41-
public function configFor($component, string $name = null): array
41+
public function configFor(string|object $name): array
4242
{
43-
if (\is_object($component)) {
44-
$component = \get_class($component);
43+
if (\is_object($name)) {
44+
$name = \get_class($name);
4545
}
4646

47-
if (!$name && class_exists($component)) {
47+
if (class_exists($name)) {
4848
$configs = [];
4949

5050
foreach ($this->config as $config) {
51-
if ($component === $config['class']) {
51+
if ($name === $config['class']) {
5252
$configs[] = $config;
5353
}
5454
}
5555

5656
if (0 === \count($configs)) {
57-
throw new \InvalidArgumentException(sprintf('Unknown component class "%s". The registered components are: %s', $component, implode(', ', array_keys($this->config))));
57+
throw new \InvalidArgumentException(sprintf('Unknown component class "%s". The registered components are: %s', $name, implode(', ', array_keys($this->config))));
5858
}
5959

6060
if (\count($configs) > 1) {
61-
throw new \InvalidArgumentException(sprintf('%d "%s" components registered with names "%s". Use the $name parameter to explicitly choose one.', \count($configs), $component, implode(', ', array_column($configs, 'name'))));
61+
throw new \InvalidArgumentException(sprintf('%d "%s" components registered with names "%s". Use the component name to explicitly choose one.', \count($configs), $name, implode(', ', array_column($configs, 'name'))));
6262
}
6363

6464
$name = $configs[0]['name'];
6565
}
6666

67-
if (!$name) {
68-
$name = $component;
69-
}
70-
7167
if (!\array_key_exists($name, $this->config)) {
7268
throw new \InvalidArgumentException(sprintf('Unknown component "%s". The registered components are: %s', $name, implode(', ', array_keys($this->config))));
7369
}

src/TwigComponent/src/ComponentRenderer.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ public function __construct(Environment $twig)
2727
$this->twig = $twig;
2828
}
2929

30-
public function render(object $component, string $template): string
30+
public function render(object $component, array $config): string
3131
{
32-
// TODO: Self-Rendering components?
33-
return $this->twig->render($template, array_merge(['this' => $component], get_object_vars($component)));
32+
return $this->twig->render($config['template'], array_merge(
33+
['this' => $component, '_component_config' => $config],
34+
get_object_vars($component)
35+
));
3436
}
3537
}

src/TwigComponent/src/Twig/ComponentRuntime.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public function render(Environment $twig, string $name, array $props = []): stri
4444

4545
return $this->componentRenderer->render(
4646
$this->componentFactory->create($name, $props),
47-
$this->componentFactory->configFor($name)['template']
47+
$this->componentFactory->configFor($name)
4848
);
4949
}
5050
}

src/TwigComponent/tests/Integration/ComponentFactoryTest.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ public function testCanGetConfigForSameComponentWithDifferentName(): void
190190
'class' => ComponentB::class,
191191
'name' => 'component_d',
192192
],
193-
$factory->configFor(new ComponentB(), 'component_d')
193+
$factory->configFor('component_d')
194194
);
195195
}
196196

@@ -200,7 +200,7 @@ public function testCannotGetConfigForComponentIfMultipleOfSameClass(): void
200200
$factory = self::getContainer()->get('ux.twig_component.component_factory');
201201

202202
$this->expectException(\InvalidArgumentException::class);
203-
$this->expectDeprecationMessage(sprintf('2 "%s" components registered with names "component_b, component_d". Use the $name parameter to explicitly choose one.', ComponentB::class));
203+
$this->expectDeprecationMessage(sprintf('2 "%s" components registered with names "component_b, component_d". Use the component name to explicitly choose one.', ComponentB::class));
204204

205205
$factory->configFor(new ComponentB());
206206
}
@@ -226,4 +226,15 @@ public function testCannotGetConfigByClassForNonRegisteredComponent(): void
226226

227227
$factory->configFor(self::class);
228228
}
229+
230+
public function testCannotGetInvalidComponent(): void
231+
{
232+
/** @var ComponentFactory $factory */
233+
$factory = self::getContainer()->get('ux.twig_component.component_factory');
234+
235+
$this->expectException(\InvalidArgumentException::class);
236+
$this->expectExceptionMessage('Unknown component "invalid". The registered components are: component_a');
237+
238+
$factory->get('invalid');
239+
}
229240
}

0 commit comments

Comments
 (0)