14
14
use Symfony \Component \HttpKernel \Exception \UnprocessableEntityHttpException ;
15
15
use Symfony \Component \PropertyAccess \Exception \UnexpectedTypeException ;
16
16
use Symfony \Component \PropertyAccess \PropertyAccessorInterface ;
17
+ use Symfony \Component \Serializer \Normalizer \DenormalizerInterface ;
18
+ use Symfony \Component \Serializer \Normalizer \NormalizerInterface ;
17
19
use Symfony \UX \LiveComponent \Attribute \AsLiveComponent ;
18
20
use Symfony \UX \LiveComponent \Attribute \LivePropContext ;
19
- use Symfony \UX \LiveComponent \Exception \UnsupportedHydrationException ;
20
21
use Symfony \UX \TwigComponent \ComponentAttributes ;
21
22
use Symfony \UX \TwigComponent \MountedComponent ;
22
23
29
30
*/
30
31
final class LiveComponentHydrator
31
32
{
33
+ public const LIVE_CONTEXT = 'live-component ' ;
32
34
private const CHECKSUM_KEY = '_checksum ' ;
33
35
private const EXPOSED_PROP_KEY = '_id ' ;
34
36
private const ATTRIBUTES_KEY = '_attributes ' ;
35
37
36
- /** @var PropertyHydratorInterface[] */
37
- private iterable $ propertyHydrators ;
38
- private PropertyAccessorInterface $ propertyAccessor ;
39
- private string $ secret ;
40
-
41
- /**
42
- * @param PropertyHydratorInterface[] $propertyHydrators
43
- */
44
- public function __construct (iterable $ propertyHydrators , PropertyAccessorInterface $ propertyAccessor , string $ secret )
45
- {
46
- $ this ->propertyHydrators = $ propertyHydrators ;
47
- $ this ->propertyAccessor = $ propertyAccessor ;
48
- $ this ->secret = $ secret ;
38
+ public function __construct (
39
+ private NormalizerInterface |DenormalizerInterface $ normalizer ,
40
+ private PropertyAccessorInterface $ propertyAccessor ,
41
+ private string $ secret
42
+ ) {
49
43
}
50
44
51
45
public function dehydrate (MountedComponent $ mounted ): array
@@ -75,6 +69,7 @@ public function dehydrate(MountedComponent $mounted): array
75
69
76
70
throw new \LogicException ($ message );
77
71
}
72
+
78
73
$ frontendPropertyNames [$ frontendName ] = $ name ;
79
74
80
75
if ($ liveProp ->isReadonly ()) {
@@ -83,13 +78,13 @@ public function dehydrate(MountedComponent $mounted): array
83
78
84
79
// TODO: improve error message if not readable
85
80
$ value = $ this ->propertyAccessor ->getValue ($ component , $ name );
86
-
87
81
$ dehydratedValue = null ;
82
+
88
83
if ($ method = $ liveProp ->dehydrateMethod ()) {
89
84
// TODO: Error checking
90
85
$ dehydratedValue = $ component ->$ method ($ value );
91
- } else {
92
- $ dehydratedValue = $ this ->dehydrateProperty ( $ value , $ name , $ component );
86
+ } elseif ( \is_object ( $ dehydratedValue = $ value )) {
87
+ $ dehydratedValue = $ this ->normalizer -> normalize ( $ dehydratedValue , ' json ' , [ self :: LIVE_CONTEXT => true ] );
93
88
}
94
89
95
90
if (\count ($ liveProp ->exposed ()) > 0 ) {
@@ -98,7 +93,7 @@ public function dehydrate(MountedComponent $mounted): array
98
93
];
99
94
foreach ($ liveProp ->exposed () as $ propertyPath ) {
100
95
$ value = $ this ->propertyAccessor ->getValue ($ component , sprintf ('%s.%s ' , $ name , $ propertyPath ));
101
- $ data [$ frontendName ][$ propertyPath ] = $ this ->dehydrateProperty ($ value , $ propertyPath , $ component ) ;
96
+ $ data [$ frontendName ][$ propertyPath ] = \is_object ( $ value ) ? $ this ->normalizer -> normalize ($ value , ' json ' , [ self :: LIVE_CONTEXT => true ]) : $ value ;
102
97
}
103
98
} else {
104
99
$ data [$ frontendName ] = $ dehydratedValue ;
@@ -168,11 +163,13 @@ public function hydrate(object $component, array $data, string $componentName):
168
163
unset($ data [$ frontendName ][self ::EXPOSED_PROP_KEY ]);
169
164
}
170
165
166
+ $ value = $ dehydratedValue ;
167
+
171
168
if ($ method = $ liveProp ->hydrateMethod ()) {
172
169
// TODO: Error checking
173
170
$ value = $ component ->$ method ($ dehydratedValue );
174
- } else {
175
- $ value = $ this ->hydrateProperty ( $ property , $ dehydratedValue );
171
+ } elseif ( $ property -> getType () instanceof \ReflectionNamedType && ! $ property -> getType ()-> isBuiltin ()) {
172
+ $ value = $ this ->normalizer -> denormalize ( $ value , $ property -> getType ()-> getName (), ' json ' , [ self :: LIVE_CONTEXT => true ] );
176
173
}
177
174
178
175
foreach ($ liveProp ->exposed () as $ exposedProperty ) {
@@ -237,57 +234,6 @@ private function verifyChecksum(array $data, array $readonlyProperties): void
237
234
}
238
235
}
239
236
240
- /**
241
- * @param scalar|array|null $value
242
- *
243
- * @return mixed
244
- */
245
- private function hydrateProperty (\ReflectionProperty $ property , $ value )
246
- {
247
- if (!$ property ->getType () || !$ property ->getType () instanceof \ReflectionNamedType || $ property ->getType ()->isBuiltin ()) {
248
- return $ value ;
249
- }
250
-
251
- foreach ($ this ->propertyHydrators as $ hydrator ) {
252
- try {
253
- return $ hydrator ->hydrate ($ property ->getType ()->getName (), $ value );
254
- } catch (UnsupportedHydrationException $ e ) {
255
- continue ;
256
- }
257
- }
258
-
259
- return $ value ;
260
- }
261
-
262
- /**
263
- * @param mixed $value
264
- *
265
- * @return scalar|array|null
266
- */
267
- private function dehydrateProperty ($ value , string $ name , object $ component )
268
- {
269
- if (is_scalar ($ value ) || \is_array ($ value ) || null === $ value ) {
270
- // nothing to dehydrate...
271
- return $ value ;
272
- }
273
-
274
- foreach ($ this ->propertyHydrators as $ hydrator ) {
275
- try {
276
- $ value = $ hydrator ->dehydrate ($ value );
277
-
278
- break ;
279
- } catch (UnsupportedHydrationException $ e ) {
280
- continue ;
281
- }
282
- }
283
-
284
- if (!is_scalar ($ value ) && !\is_array ($ value ) && null !== $ value ) {
285
- throw new \LogicException (sprintf ('Cannot dehydrate property "%s" of "%s". The value "%s" does not have a dehydrator. ' , $ name , \get_class ($ component ), get_debug_type ($ value )));
286
- }
287
-
288
- return $ value ;
289
- }
290
-
291
237
/**
292
238
* Transforms a path like `post.name` into `[post][name]`.
293
239
*
0 commit comments