1414use Symfony \Component \HttpKernel \Exception \UnprocessableEntityHttpException ;
1515use Symfony \Component \PropertyAccess \Exception \UnexpectedTypeException ;
1616use Symfony \Component \PropertyAccess \PropertyAccessorInterface ;
17+ use Symfony \Component \Serializer \Normalizer \DenormalizerInterface ;
18+ use Symfony \Component \Serializer \Normalizer \NormalizerInterface ;
1719use Symfony \UX \LiveComponent \Attribute \AsLiveComponent ;
1820use Symfony \UX \LiveComponent \Attribute \LivePropContext ;
19- use Symfony \UX \LiveComponent \Exception \UnsupportedHydrationException ;
2021use Symfony \UX \TwigComponent \ComponentAttributes ;
2122use Symfony \UX \TwigComponent \MountedComponent ;
2223
2930 */
3031final class LiveComponentHydrator
3132{
33+ public const LIVE_CONTEXT = 'live-component ' ;
3234 private const CHECKSUM_KEY = '_checksum ' ;
3335 private const EXPOSED_PROP_KEY = '_id ' ;
3436 private const ATTRIBUTES_KEY = '_attributes ' ;
3537
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+ ) {
4943 }
5044
5145 public function dehydrate (MountedComponent $ mounted ): array
@@ -75,6 +69,7 @@ public function dehydrate(MountedComponent $mounted): array
7569
7670 throw new \LogicException ($ message );
7771 }
72+
7873 $ frontendPropertyNames [$ frontendName ] = $ name ;
7974
8075 if ($ liveProp ->isReadonly ()) {
@@ -83,13 +78,13 @@ public function dehydrate(MountedComponent $mounted): array
8378
8479 // TODO: improve error message if not readable
8580 $ value = $ this ->propertyAccessor ->getValue ($ component , $ name );
86-
8781 $ dehydratedValue = null ;
82+
8883 if ($ method = $ liveProp ->dehydrateMethod ()) {
8984 // TODO: Error checking
9085 $ 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 ] );
9388 }
9489
9590 if (\count ($ liveProp ->exposed ()) > 0 ) {
@@ -98,7 +93,7 @@ public function dehydrate(MountedComponent $mounted): array
9893 ];
9994 foreach ($ liveProp ->exposed () as $ propertyPath ) {
10095 $ 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 ;
10297 }
10398 } else {
10499 $ data [$ frontendName ] = $ dehydratedValue ;
@@ -168,11 +163,13 @@ public function hydrate(object $component, array $data, string $componentName):
168163 unset($ data [$ frontendName ][self ::EXPOSED_PROP_KEY ]);
169164 }
170165
166+ $ value = $ dehydratedValue ;
167+
171168 if ($ method = $ liveProp ->hydrateMethod ()) {
172169 // TODO: Error checking
173170 $ 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 ] );
176173 }
177174
178175 foreach ($ liveProp ->exposed () as $ exposedProperty ) {
@@ -237,57 +234,6 @@ private function verifyChecksum(array $data, array $readonlyProperties): void
237234 }
238235 }
239236
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-
291237 /**
292238 * Transforms a path like `post.name` into `[post][name]`.
293239 *
0 commit comments