Skip to content

Commit ce4d954

Browse files
committed
hard-code attributes
1 parent f8e2415 commit ce4d954

File tree

5 files changed

+57
-31
lines changed

5 files changed

+57
-31
lines changed

src/LiveComponent/src/LiveComponentHydrator.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
1818
use Symfony\UX\LiveComponent\Attribute\LivePropContext;
1919
use Symfony\UX\LiveComponent\Exception\UnsupportedHydrationException;
20+
use Symfony\UX\TwigComponent\ComponentAttributes;
2021
use Symfony\UX\TwigComponent\ComponentMetadata;
2122
use Symfony\UX\TwigComponent\MountedComponent;
2223

@@ -31,6 +32,7 @@ final class LiveComponentHydrator
3132
{
3233
private const CHECKSUM_KEY = '_checksum';
3334
private const EXPOSED_PROP_KEY = '_id';
35+
private const ATTRIBUTES_KEY = '_attributes';
3436

3537
/** @var PropertyHydratorInterface[] */
3638
private iterable $propertyHydrators;
@@ -104,6 +106,11 @@ public function dehydrate(MountedComponent $mounted): array
104106
}
105107
}
106108

109+
if ($attributes = $mounted->getAttributes()->all()) {
110+
$data[self::ATTRIBUTES_KEY] = $attributes;
111+
$readonlyProperties[] = self::ATTRIBUTES_KEY;
112+
}
113+
107114
$data[self::CHECKSUM_KEY] = $this->computeChecksum($data, $readonlyProperties);
108115

109116
return $data;
@@ -113,6 +120,10 @@ public function hydrate(object $component, array $data, ComponentMetadata $metad
113120
{
114121
$readonlyProperties = [];
115122

123+
if (isset($data[self::ATTRIBUTES_KEY])) {
124+
$readonlyProperties[] = self::ATTRIBUTES_KEY;
125+
}
126+
116127
/** @var LivePropContext[] $propertyContexts */
117128
$propertyContexts = iterator_to_array(AsLiveComponent::liveProps($component));
118129

@@ -133,7 +144,9 @@ public function hydrate(object $component, array $data, ComponentMetadata $metad
133144

134145
$this->verifyChecksum($data, $readonlyProperties);
135146

136-
unset($data[self::CHECKSUM_KEY]);
147+
$attributes = new ComponentAttributes($data[self::ATTRIBUTES_KEY] ?? []);
148+
149+
unset($data[self::CHECKSUM_KEY], $data[self::ATTRIBUTES_KEY]);
137150

138151
foreach ($propertyContexts as $context) {
139152
$property = $context->reflectionProperty();
@@ -192,7 +205,7 @@ public function hydrate(object $component, array $data, ComponentMetadata $metad
192205
$component->{$method->name}();
193206
}
194207

195-
return new MountedComponent($component, $metadata);
208+
return new MountedComponent($component, $attributes, $metadata);
196209
}
197210

198211
private function computeChecksum(array $data, array $readonlyProperties): string

src/LiveComponent/tests/Integration/LiveComponentHydratorTest.php

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
use Symfony\UX\LiveComponent\Tests\Fixture\Component\Component1;
1818
use Symfony\UX\LiveComponent\Tests\Fixture\Component\Component2;
1919
use Symfony\UX\LiveComponent\Tests\Fixture\Component\Component3;
20-
use Symfony\UX\LiveComponent\Tests\Fixture\Component\ComponentWithAttributes;
2120
use Symfony\UX\LiveComponent\Tests\Fixture\Entity\Entity1;
21+
use Symfony\UX\TwigComponent\ComponentAttributes;
2222
use Symfony\UX\TwigComponent\ComponentFactory;
2323
use Symfony\UX\TwigComponent\ComponentMetadata;
2424
use Symfony\UX\TwigComponent\MountedComponent;
@@ -261,7 +261,7 @@ public function testCanDehydrateAndHydrateArrays(): void
261261
$instance = clone $component;
262262
$instance->prop = ['some', 'array'];
263263

264-
$dehydrated = $hydrator->dehydrate(new MountedComponent($instance, new ComponentMetadata([])));
264+
$dehydrated = $hydrator->dehydrate(new MountedComponent($instance, new ComponentAttributes([]), new ComponentMetadata([])));
265265

266266
$this->assertArrayHasKey('prop', $dehydrated);
267267
$this->assertSame($instance->prop, $dehydrated['prop']);
@@ -283,19 +283,16 @@ public function testCanDehydrateAndHydrateComponentsWithAttributes(): void
283283

284284
$mounted = $factory->create('with_attributes', $attributes = ['class' => 'foo']);
285285

286-
/** @var ComponentWithAttributes $component */
287-
$component = $mounted->getComponent();
288-
289-
$this->assertSame($attributes, $component->attributes->all());
286+
$this->assertSame($attributes, $mounted->getAttributes()->all());
290287

291288
$dehydrated = $hydrator->dehydrate($mounted);
292289

293-
$this->assertArrayHasKey('attributes', $dehydrated);
294-
$this->assertSame($attributes, $dehydrated['attributes']);
290+
$this->assertArrayHasKey('_attributes', $dehydrated);
291+
$this->assertSame($attributes, $dehydrated['_attributes']);
295292

296-
$hydrator->hydrate($component = $factory->get('with_attributes'), $dehydrated, $mounted->getMetadata());
293+
$mounted = $hydrator->hydrate($factory->get('with_attributes'), $dehydrated, $mounted->getMetadata());
297294

298-
$this->assertSame($attributes, $component->attributes->all());
295+
$this->assertSame($attributes, $mounted->getAttributes()->all());
299296
}
300297

301298
public function testCanDehydrateAndHydrateComponentsWithEmptyAttributes(): void
@@ -308,20 +305,14 @@ public function testCanDehydrateAndHydrateComponentsWithEmptyAttributes(): void
308305

309306
$mounted = $factory->create('with_attributes');
310307

311-
/** @var ComponentWithAttributes $component */
312-
$component = $mounted->getComponent();
313-
314-
$this->assertSame([], $component->attributes->all());
308+
$this->assertSame([], $mounted->getAttributes()->all());
315309

316310
$dehydrated = $hydrator->dehydrate($mounted);
317311

318-
$this->assertArrayHasKey('attributes', $dehydrated);
319-
$this->assertSame([], $dehydrated['attributes']);
320-
321-
$component = $factory->get('with_attributes');
312+
$this->assertArrayNotHasKey('_attributes', $dehydrated);
322313

323-
$hydrator->hydrate($component, $dehydrated, $mounted->getMetadata());
314+
$mounted = $hydrator->hydrate($factory->get('with_attributes'), $dehydrated, $mounted->getMetadata());
324315

325-
$this->assertSame([], $component->attributes->all());
316+
$this->assertSame([], $mounted->getAttributes()->all());
326317
}
327318
}

src/TwigComponent/src/ComponentFactory.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,22 @@ public function create(string $name, array $data = []): MountedComponent
6565

6666
$data = $this->postMount($component, $data);
6767

68-
foreach ($data as $property => $value) {
69-
throw new \LogicException(sprintf('Unable to write "%s" to component "%s". Make sure this is a writable property or create a mount() with a $%s argument.', $property, \get_class($component), $property));
68+
// create attributes from "attributes" key if exists
69+
$attributes = $data['attributes'] ?? [];
70+
unset($data['attributes']);
71+
72+
// ensure remaining data is scalar
73+
foreach ($data as $key => $value) {
74+
if (!is_scalar($value)) {
75+
throw new \LogicException(sprintf('Unable to use "%s" (%s) as an attribute. Attributes must be scalar. If you meant to mount this value on "%s", make sure $%s is a writable property.', $key, get_debug_type($value), $component::class, $key));
76+
}
7077
}
7178

72-
return new MountedComponent($component, $this->metadataFor($name));
79+
return new MountedComponent(
80+
$component,
81+
new ComponentAttributes(array_merge($attributes, $data)),
82+
$this->metadataFor($name)
83+
);
7384
}
7485

7586
/**

src/TwigComponent/src/MountedComponent.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,33 @@
2020
*/
2121
final class MountedComponent
2222
{
23-
public function __construct(private object $component, private ComponentMetadata $metadata)
24-
{
23+
public function __construct(
24+
private object $component,
25+
private ComponentAttributes $attributes,
26+
private ComponentMetadata $metadata
27+
) {
2528
}
2629

2730
public function getComponent(): object
2831
{
2932
return $this->component;
3033
}
3134

35+
public function getAttributes(): ComponentAttributes
36+
{
37+
return $this->attributes;
38+
}
39+
3240
public function getMetadata(): ComponentMetadata
3341
{
3442
return $this->metadata;
3543
}
3644

3745
public function getVariables(): array
3846
{
39-
return array_merge(['this' => $this->component], get_object_vars($this->component));
47+
return array_merge(
48+
['this' => $this->component, 'attributes' => $this->attributes],
49+
get_object_vars($this->component)
50+
);
4051
}
4152
}

src/TwigComponent/tests/Integration/ComponentFactoryTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ public function testExceptionThrownIfRequiredMountParameterIsMissingFromPassedDa
105105
$factory->create('component_c');
106106
}
107107

108-
public function testExceptionThrownIfUnableToWritePassedDataToProperty(): void
108+
public function testExceptionThrownIfUnableToWritePassedDataToPropertyAndIsNotScalar(): void
109109
{
110110
/** @var ComponentFactory $factory */
111111
$factory = self::getContainer()->get('ux.twig_component.component_factory');
112112

113113
$this->expectException(\LogicException::class);
114-
$this->expectExceptionMessage('Unable to write "service" to component "Symfony\UX\TwigComponent\Tests\Fixture\Component\ComponentA". Make sure this is a writable property or create a mount() with a $service argument.');
114+
$this->expectExceptionMessage(sprintf('Unable to use "service" (stdClass) as an attribute. Attributes must be scalar. If you meant to mount this value on "%s", make sure $service is a writable property.', ComponentA::class));
115115

116-
$factory->create('component_a', ['propB' => 'B', 'service' => 'invalid']);
116+
$factory->create('component_a', ['propB' => 'B', 'service' => new \stdClass()]);
117117
}
118118

119119
public function testTwigComponentServiceTagMustHaveKey(): void

0 commit comments

Comments
 (0)