@@ -245,6 +245,15 @@ exceptions being properties that hold services (these don't need to be
245245stateful because they will be autowired each time before the component
246246is rendered).
247247
248+ LiveProp Data Types
249+ ~~~~~~~~~~~~~~~~~~~
250+
251+ LiveProps must be a value that can be sent to JavaScript. Supported values
252+ are scalars (int, float, string, bool, null), arrays (of scalar values), enums,
253+ DateTime objects & Doctrine entity objects.
254+
255+ See :ref: `hydration ` for handling more complex data.
256+
248257Data Binding
249258------------
250259
@@ -404,11 +413,16 @@ update correctly because JavaScript doesn't trigger the normal
404413JavaScript library and set the model directly (or trigger a
405414``change `` event on the ``data-model `` field). See :ref: `working-in-javascript `.
406415
416+ .. _hydration :
417+
407418LiveProp for Entities & More Complex Data
408419-----------------------------------------
409420
410- You can also add the ``LiveProp `` attribute above more complex data,
411- like entities or other objects. For example::
421+ ``LiveProp `` data must be simple scalar values, with a few exception,
422+ like ``DateTime `` objects, enums & Doctrine entity objects. When ``LiveProp``s
423+ are sent to the frontend, they are "dehydrated". When Ajax requests are sent
424+ to the frontend, the dehydrated data is then "hydrated" back into the original.
425+ Doctrine entity objects are a special case for ``LiveProp ``::
412426
413427 use App\E ntity\P ost;
414428
@@ -419,25 +433,24 @@ like entities or other objects. For example::
419433 public Post $post;
420434 }
421435
422- To send it to the frontend, the ``Post `` object is "dehydrated" to a scalar value.
423- For persisted entities, the data is dehydrated to its ``id ``. For unsaved entities -
424- or any other objects - the value is passed through Symfony's serializer.
425-
426- When Ajax requests are sent to the frontend, the dehydrated data is then *hydrated *
427- back into the original values.
436+ If the ``Post `` object is persisted, its dehydrated to the entity's ``id `` and then
437+ hydrated back by querying the database. If the object is unpersisted, it's dehydrated
438+ to an empty array, then hydrated back by creating an *empty * object
439+ (i.e. ``new Post() ``).
428440
429- .. caution ::
441+ Arrays of Doctrine entities and other "simple" values like ``DateTime `` are also
442+ supported, as long as the ``LiveProp `` has proper PHPDoc that LiveComponents
443+ can read::
430444
431- Dehydrated data is passed to the frontend so it's readable by the user.
432- If your object is dehydrated via the serializer, be sure no sensitive
433- data is exposed.
445+ /** @var Product[] */
446+ public $products = [];
434447
435448Writable Object Properties or Array Keys
436449~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
437450
438- By default, the user can't change the *properties * of an object ``LiveProp ``
439- (or the keys of an array). But, you can allow this by setting
440- `` writable `` to property names that * should * be writable::
451+ By default, the user can't change the *properties * of an entity ``LiveProp ``
452+ You can allow this by setting `` writable `` to property names that * should * be writable.
453+ This also works as a way to make only * some * keys of an array writable::
441454
442455 use App\Entity\Post;
443456
@@ -549,6 +562,26 @@ single value or an array of values::
549562 <option value="sushi">Sushi</option>
550563 </select>
551564
565+ LiveProp Date Formats
566+ ~~~~~~~~~~~~~~~~~~~~~
567+
568+ .. versionadded :: 2.8
569+
570+ The ``format `` option was introduced in Live Components 2.8.
571+
572+ If you have a writable ``LiveProp `` that is some sort of ``DateTime `` instance,
573+ you can control the format of the model on the frontend with the ``format ``
574+ option::
575+
576+ #[LiveProp(writable: true, format: 'Y-m-d')]
577+ public ?\DateTime $publishOn = null;
578+
579+ Now you can bind this to a field on the frontend that uses that same format:
580+
581+ .. code-block :: twig
582+
583+ <input type="date" data-model="publishOn">
584+
552585 Allowing an Entity to be Changed to Another
553586~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
554587
@@ -596,36 +629,37 @@ Note that being able to change the "identity" of an object is something
596629that works only for objects that are dehydrated to a scalar value (like
597630persisted entities, which dehydrate to an ``id ``).
598631
599- More Control Over Dehydration & Hydration
600- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
632+ Hydration, DTO's & the Serializer
633+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
601634
602- The ``LiveProp `` attribute uses the normalizer to dehydrate values before
603- sending them to the frontend. Then it uses the denormalizer to hydrate values
604- when they are sent back to the backend.
635+ If you try to use a ``LiveProp `` for some unsupported type (e.g.a DTO object),
636+ it will fail. A best practice is to use simple data.
605637
606- If needed, you can control the normalization or denormalization context using
607- the ``Context `` attribute from Symfony's serializer::
638+ But there are two options to make this work:
608639
609- use App\Entity\Post;
640+ 1) Hydrating with the Serializer
641+ ................................
610642
611- #[AsLiveComponent('edit_post')]
612- class EditPostComponent
643+ .. versionadded :: 2.8
644+
645+ The ``useSerializerForHydration `` option was added in LiveComponent 2.8.
646+
647+ To hydrate/dehydrate through Symfony's serializer, use the ``useSerializerForHydration ``
648+ option::
649+
650+ class ComponentWithAddressDto
613651 {
614- #[LiveProp]
615- #[Context(groups: ['my_group'])]
616- public Post $post;
652+ #[LiveProp(useSerializerForHydration: true)]
653+ public AddressDto $addressDto;
617654 }
618655
619- .. note ::
656+ You can also set a `` serializationContext `` option on the `` LiveProp ``.
620657
621- If your property has writable paths, those will be normalized/denormalized
622- using the same ` Context ` set on the property itself .
658+ 2) Hydrating with Methods: hydrateWith & dehydrateWith
659+ ..................................................... .
623660
624- Using the serializer isn't meant to work out-of-the-box in every possible situation
625- and it's always simpler to use scalar `LiveProp ` values instead of complex objects.
626- If you're having (de)hydrating a complex object, you can take full control by
627- setting the ``hydrateWith `` and ``dehydrateWith `` options on ``LiveProp ``. For
628- example::
661+ You can take full control of the hydration process by setting the ``hydrateWith ``
662+ and ``dehydrateWith `` options on ``LiveProp ``::
629663
630664 class ComponentWithAddressDto
631665 {
@@ -641,12 +675,55 @@ example::
641675 ];
642676 }
643677
644- public function hydrateMyDto ($data): MyDto
678+ public function hydrateAddress ($data): AddressDto
645679 {
646- return new MyDto ($data['street'], $data['city'], $data['state']);
680+ return new AddressDto ($data['street'], $data['city'], $data['state']);
647681 }
648682 }
649683
684+ Hydration Extensions
685+ ~~~~~~~~~~~~~~~~~~~~
686+
687+ .. versionadded :: 2.8
688+
689+ The ``HydrationExtensionInterface `` system was added in LiveComponents 2.8.
690+
691+ If you frequently hydrate/dehydrate the same type of object, you can create a custom
692+ hydration extension to make this easier. For example, if you frequently hydrate
693+ a custom ``Food `` object, a hydration extension might look like this::
694+
695+ use App\Model\Food;
696+ use Symfony\UX\LiveComponent\Hydration\HydrationExtensionInterface;
697+
698+ class FoodHydrationExtension implements HydrationExtensionInterface
699+ {
700+ public function supports(string $className): bool
701+ {
702+ return is_subclass_of($className, Food::class);
703+ }
704+
705+ public function hydrate($value)
706+ {
707+ return new Food($value['name'], $value['isCooked']);
708+ }
709+
710+ public function dehydrate(object $object): mixed
711+ {
712+ return [
713+ 'name' => $object->getName(),
714+ 'isCooked' => $object->isCooked(),
715+ ];
716+ }
717+ }
718+
719+ If you're using autoconfiguration, you're done! Otherwise, tag the service
720+ with ``live_component.hydration_extension ``.
721+
722+ .. tip ::
723+
724+ Internally, Doctrine entity objects use the ``DoctrineEntityHydrationExtension ``
725+ to control the custom (de)hydration of entity objects.
726+
650727Updating a Model Manually
651728-------------------------
652729
@@ -791,9 +868,7 @@ Or, to *hide* an element while the component is loading:
791868.. code-block :: html+twig
792869
793870 <!-- hide when the component is loading -->
794- <span
795- data-loading="hide"
796- >Saved!</span>
871+ <span data-loading="hide">Saved!</span>
797872
798873Adding and Removing Classes or Attributes
799874~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 commit comments