Share
Embed
Serialización en Symfony con JMSSerializerBundle y el componente Serializer
Tener un buen dominio de la serialización es algo fundamental para construir APIs limpias.
Tanto el componente Serializer de Symfony como el JMSSerializerBundle son dos herramientas muy potentes que nos facilitan la tarea de crear diferentes representaciones de nuestros modelos y crear o actualizar esos objetos a partir de sus representaciones.
En esta charla explicaremos cómo funcionan y cómo se utilizan tanto el componente como el bundle, veremos casos de uso interesantes y analizaremos qué fortalezas tiene cada uno de ellos y en qué ocasiones o para qué tareas concretas puede resultar más cómodo uno u otro. Todo ello enfocado desde un punto de vista práctico, con ejemplos que ayuden a ilustrar los distintos usos y situaciones.
Victoria Quirante
July 01, 2017
Transcript
164 slides
-
Trabajo en Limenius Ofrecemos formación, consultoría y desarrollo a empresas
Trabajamos con Symfony y React No hay proyecto que no tenga una API El serializador es uno de tus mejores amigos al implementar una API Victoria Quirante @vicqr [email protected] -
@vicqr Serialización Es el proceso de codificación de un objeto
en otro formato que puede ser almacenado, transmitido y posteriormente descodificado -
@vicqr Serialización Es el proceso de codificación de un objeto
en otro formato que puede ser almacenado, transmitido y posteriormente descodificado OBJETOS REPRESENTACIONES (JSON, XML..) -
@vicqr Serialización Es el proceso de codificación de un objeto
en otro formato que puede ser almacenado, transmitido y posteriormente descodificado OBJETOS REPRESENTACIONES (JSON, XML..) Serialización -
@vicqr Serialización Es el proceso de codificación de un objeto
en otro formato que puede ser almacenado, transmitido y posteriormente descodificado OBJETOS REPRESENTACIONES (JSON, XML..) Serialización Deserialización -
@vicqr Ejemplo de serialización $responseData = [ 'id' => $spaceship->getId(),
'name' => $spaceship->getName(), 'color' => $spaceship->getColor(), 'maxSpeed' => $spaceship->getMaxSpeed(), ]; $response = new JsonResponse($responseData, 201); Nuestro objeto -> La response (serialización manual) -
@vicqr Ejemplo de serialización $responseData = [ 'id' => $spaceship->getId(),
'name' => $spaceship->getName(), 'color' => $spaceship->getColor(), 'maxSpeed' => $spaceship->getMaxSpeed(), ]; $response = new JsonResponse($responseData, 201); ... ... $spaceship = new Spaceship(); $spaceship->setName($content['name']); $spaceship->setColor($content['color']); $spaceship->setMaxSpeed($content['maxSpeed']); Nuestro objeto -> La response (serialización manual) La request -> Nuestro objeto (deserialización manual) -
@vicqr Ejemplo de serialización $responseData = [ 'id' => $spaceship->getId(),
'name' => $spaceship->getName(), 'color' => $spaceship->getColor(), 'maxSpeed' => $spaceship->getMaxSpeed(), ]; $response = new JsonResponse($responseData, 201); ... ... $spaceship = new Spaceship(); $spaceship->setName($content['name']); $spaceship->setColor($content['color']); $spaceship->setMaxSpeed($content['maxSpeed']); Apasionante :_( Nuestro objeto -> La response (serialización manual) La request -> Nuestro objeto (deserialización manual) -
@vicqr Serialización con serializador $responseData = [ 'id' => $spaceship->getId(),
'name' => $spaceship->getName(), 'color' => $spaceship->getColor(), 'maxSpeed' => $spaceship->getMaxSpeed(), ]; $response = new JsonResponse($responseData, 201); Convirtiendo nuestro objeto en una Response en JSON -
@vicqr Serialización con serializador $responseData = [ 'id' => $spaceship->getId(),
'name' => $spaceship->getName(), 'color' => $spaceship->getColor(), 'maxSpeed' => $spaceship->getMaxSpeed(), ]; $response = new JsonResponse($responseData, 201); -------------- $response = new Response($serializer->serialize($spaceship, 'json'), 201); Convirtiendo nuestro objeto en una Response en JSON -
@vicqr Deserialización con serializador $spaceship = new Spaceship(); $spaceship->setName($content['name']); $spaceship->setColor($content['color']);
$spaceship->setMaxSpeed($content['maxSpeed']); Convirtiendo el contenido de la Request (JSON) en un objeto -
@vicqr Deserialización con serializador $spaceship = new Spaceship(); $spaceship->setName($content['name']); $spaceship->setColor($content['color']);
$spaceship->setMaxSpeed($content['maxSpeed']); ------------------ $spaceship = $serializer->deserialize($content, Spaceship::class, 'json'); Convirtiendo el contenido de la Request (JSON) en un objeto -
@vicqr El serializador Es un código que es muy bueno
serializando por nosotros -
@vicqr El serializador Es un código que es muy bueno
serializando por nosotros -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” No quiero mostrarlo -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” No quiero mostrarlo No quiero mostrarlo en el list -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” No quiero mostrarlo No quiero mostrarlo en el list Quiero añadir “thumb_” -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” No quiero mostrarlo No quiero mostrarlo en el list Quiero añadir “thumb_” Solo en la versión 2 de la API -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” No quiero mostrarlo No quiero mostrarlo en el list Quiero añadir “thumb_” Solo en la versión 2 de la API Quiero que sea un campo más -
@vicqr Representaciones != lo que hay en la BD {
id: 18, name: "Apolo18", budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” No quiero mostrarlo No quiero mostrarlo en el list Quiero añadir “thumb_” Solo en la versión 2 de la API Quiero que sea un campo más Con este tipo de cosas es con lo que va a ayudarnos un serializador -
@vicqr Datos básicos JMSSERIALIZERBUNDLE - Es un bundle (y librería,
claro) - Creado por @schmittjoh - Se comenzó en abril de 2011 - Pasó época de poco mantenimiento - Desde hace un año mejor (@goetas_asmir) - Licencia Apache SYMFONY SERIALIZER -
@vicqr Datos básicos JMSSERIALIZERBUNDLE SYMFONY SERIALIZER - Es un bundle
(y librería, claro) - Creado por @schmittjoh - Se comenzó en abril de 2011 - Pasó época de poco mantenimiento - Desde hace un año mejor (@goetas_asmir) - Licencia Apache - Es un Symfony Component - Desarrollado por los core - Se comenzó en enero de 2011 - Se le criticaba que “le faltan muchas cosas” - En continua evolución y mejora - Licencia MIT -
@vicqr Inciso: componentes y bundles - Componente -> librería desacoplada
y reutilizable - Bundle -> atado al Symfony Framework -
@vicqr Inciso: componentes y bundles - Componente -> librería desacoplada
y reutilizable - Bundle -> atado al Symfony Framework A menudo tenemos: Funcionalidad Librería Integración con SF Configuración Inyección de dependencias Bundle + -
@vicqr Inciso: componentes y bundles - Componente -> librería desacoplada
y reutilizable - Bundle -> atado al Symfony Framework A menudo tenemos: Por ejemplo, en el caso del JMSSerializerBundle Funcionalidad Librería Integración con SF Configuración Inyección de dependencias Bundle + -
@vicqr JMSSerialiazerBundle - Características - Muchas anotaciones útiles que cubren
casos de uso muy frecuentes - Muy sencillo empezar a utilizarlo -
@vicqr JMSSerialiazer - Instalación y setup - Instalación: - composer
require - Registrar el bundle http://jmsyst.com/bundles/JMSSerializerBundle -
@vicqr JMSSerialiazer - Instalación y setup - Instalación: - composer
require - Registrar el bundle - Setup: no hay que hacer nada para empezar a usarlo http://jmsyst.com/bundles/JMSSerializerBundle -
@vicqr Componente Serializer - Características - No aspira a resolver
casos de uso concretos sino a proporcionar una arquitectura que te permita hacerlo - Menos “ya hecho”, más flexible Aproximación diferente -
@vicqr Componente Serializer - Características El trabajo más complejo se
lleva a cabo en los normalizadores -
@vicqr SF Serializer - Instalación y setup - Instalación: -
composer require - Si quieres usar ObjectNormalizer instalas también PropertyAccess -
@vicqr SF Serializer - Instalación y setup - Instalación: -
composer require - Si quieres usar ObjectNormalizer instalas también PropertyAccess - Setup: hay que hacer algunas cosas -
@vicqr Componente Serializer - Setup use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use
Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; $encoders = array(new XmlEncoder(), new JsonEncoder()); $normalizers = array(new ObjectNormalizer()); serializer = new Serializer($normalizers, $encoders); -
@vicqr Componente Serializer - Setup use Symfony\Component\Serializer\Serializer; use Symfony\Component\Serializer\Encoder\XmlEncoder; use
Symfony\Component\Serializer\Encoder\JsonEncoder; use Symfony\Component\Serializer\Normalizer\ObjectNormalizer; $encoders = array(new XmlEncoder(), new JsonEncoder()); $normalizers = array(new ObjectNormalizer()); serializer = new Serializer($normalizers, $encoders); El ObjectNormalizer es muy potente, pero no idóneo para todos los casos -
@vicqr JMSSerialiazerBundle - Deserialización # AppBundle/Controller/Api/SpaceshipController.php /** * @Route("/api/spaceships") *
@Method("POST") */ public function newAction(Request $request) { $serializer = $this->container->get('jms_serializer'); $spaceship = $serializer->deserialize($request->getContent(), Spaceship::class, 'json'); $em = $this->getDoctrine()->getManager(); $em->persist($spaceship); $em->flush(); return new Response("Spaceship created!"); } -
@vicqr JMSSerialiazerBundle - Deserialización # AppBundle/Controller/Api/SpaceshipController.php /** * @Route("/api/spaceships") *
@Method("POST") */ public function newAction(Request $request) { $serializer = $this->container->get('jms_serializer'); $spaceship = $serializer->deserialize($request->getContent(), Spaceship::class, 'json'); $em = $this->getDoctrine()->getManager(); $em->persist($spaceship); $em->flush(); return new Response("Spaceship created!"); } -
@vicqr JMSSerialiazerBundle - Serialización # AppBundle/Controller/Api/SpaceshipController.php /** * @Route("/api/spaceships/{name}") *
@Method("GET") */ public function showAction($name) { $spaceship = $this->getDoctrine() ->getRepository('AppBundle:Spaceship') ->findOneByName($name); $serializer = $this->container->get('jms_serializer'); return new Response($serializer->serialize($spaceship, 'json'), 200); } -
@vicqr JMSSerialiazerBundle - Serialización # AppBundle/Controller/Api/SpaceshipController.php /** * @Route("/api/spaceships/{name}") *
@Method("GET") */ public function showAction($name) { $spaceship = $this->getDoctrine() ->getRepository('AppBundle:Spaceship') ->findOneByName($name); $serializer = $this->container->get('jms_serializer'); return new Response($serializer->serialize($spaceship, 'json'), 200); } -
@vicqr JMSSerialiazerBundle - Serialización HTTP/1.1 200 OK Host: localhost:8000 Connection:
close X-Powered-By: PHP/7.1.0-0ubuntu0.16.04.4 Cache-Control: no-cache, private Date: Sat, 24 Jun 2017 17:45:32 GMT Content-Type: application/json X-Debug-Token: 127f2b X-Debug-Token-Link: http://localhost:8000/_profiler/127f2b {"id":1,"name":"Orion","color":"Razzmic Berry","maxSpeed":8900} -
@vicqr Componente Serializer - Deserialización # AppBundle/Controller/Api/SpaceshipController.php /** * @Route("/api/spaceships")
* @Method("POST") */ public function newAction(Request $request) { $encoders = array(new JsonEncoder()); $normalizers = array(new ObjectNormalizer()); $serializer = new Serializer($normalizers, $encoders); $spaceship = $serializer->deserialize($request->getContent(), Spaceship::class, 'json'); $em = $this->getDoctrine()->getManager(); $em->persist($spaceship); $em->flush(); return new Response("Spaceship created!"); } -
@vicqr Componente Serializer - Deserialización # AppBundle/Controller/Api/SpaceshipController.php /** * @Route("/api/spaceships")
* @Method("POST") */ public function newAction(Request $request) { $encoders = array(new JsonEncoder()); $normalizers = array(new ObjectNormalizer()); $serializer = new Serializer($normalizers, $encoders); $spaceship = $serializer->deserialize($request->getContent(), Spaceship::class, 'json'); $em = $this->getDoctrine()->getManager(); $em->persist($spaceship); $em->flush(); return new Response("Spaceship created!"); } -
@vicqr Deserializar un objeto ya existente # AppBundle/Controller/Api/SpaceshipController.php $serializer->deserialize($request->getContent(), Spaceship::class,
'json', array('object_to_populate' => $spaceship)); -
@vicqr Componente Serializer - Serialización # AppBundle/Controller/Api/SpaceshipController.php /** * @Route("/api/spaceships/{name}")
* @Method("GET") */ public function showAction($name) { $encoders = array(new JsonEncoder()); $normalizers = array(new ObjectNormalizer()); $serializer = new Serializer($normalizers, $encoders); $spaceship = $this->getDoctrine() ->getRepository('AppBundle:Spaceship') ->findOneByName($name); return new Response($serializer->serialize($spaceship, 'json'), 200); } -
@vicqr Comparación inicial Algo de instalación -mínimo- el bundle Algo
más de configuración el componente En general, para este tipo de ejemplo, idéntico uso -
@vicqr Comparación inicial Algo de instalación -mínimo- el bundle Algo
más de configuración el componente En general, para este tipo de ejemplo, idéntico uso Pero este tipo de ejemplo no existe -
@vicqr JMSSerializerBundle Tres estrategias de exclusión (Exclude, Groups, Versions) Propiedades
configurables (Virtual Props., Accessors) Eventos para más flexibilidad XML muy configurable Muchas funcionalidades que necesitas siempre listas para usar -
@vicqr Estrategias de exclusión - Estrategias generales: @ExclusionPolicy (all, none),
@Exclude, @Expose - Versiones: @Until, @Since -
@vicqr Estrategias de exclusión - Estrategias generales: @ExclusionPolicy (all, none),
@Exclude, @Expose - Versiones: @Until, @Since - Diferentes vistas de un objeto: @Groups -
@vicqr Estrategias de exclusión - Estrategias generales: @ExclusionPolicy (all, none),
@Exclude, @Expose - Versiones: @Until, @Since - Diferentes vistas de un objeto: @Groups - Limitar nivel de serialización en el grafo: @MaxDepth -
@vicqr Estrategias de exclusión - Estrategias generales: @ExclusionPolicy (all, none),
@Exclude, @Expose - Versiones: @Until, @Since - Diferentes vistas de un objeto: @Groups - Limitar nivel de serialización en el grafo: @MaxDepth - Exclusión dinámica: @Exclude(if=”...”) -
@vicqr Estrategias de exclusión - Estrategias generales: @ExclusionPolicy (all, none),
@Exclude, @Expose - Versiones: @Until, @Since - Diferentes vistas de un objeto: @Groups - Limitar nivel de serialización en el grafo: @MaxDepth - Exclusión dinámica: @Exclude(if=”...”) Muchas formas de indicar qué propiedades muestras y cuáles no -
@vicqr Propiedades configurables - Cambiar nombre de una propiedad: @SerializedName
- Crear propiedades virtuales: @VirtualProperty -
@vicqr Propiedades configurables - Cambiar nombre de una propiedad: @SerializedName
- Crear propiedades virtuales: @VirtualProperty Sencillo cambiar lo que tengo por defecto en las propiedades -
@vicqr Accessors - De qué modo hay que acceder las
propiedades: @AccessorType - Qué método hay que utilizar para acceder una propiedad: @Accessor - En qué orden hay que acceder las propiedades: @AccessorOrder -
@vicqr Accessors - De qué modo hay que acceder las
propiedades: @AccessorType - Qué método hay que utilizar para acceder una propiedad: @Accessor - En qué orden hay que acceder las propiedades: @AccessorOrder Sencillo configurar el acceso a las propiedades -
@vicqr XML muy configurable - @XmlRoot - @XmlAttribute - @XmlDiscriminator
- @XmlValue - @XmlList - @XmlMap - @XmlKeyValuePairs - @XmlAttributeMap - @XmlElement - @XmlNamespace -
@vicqr XML muy configurable - @XmlRoot - @XmlAttribute - @XmlDiscriminator
- @XmlValue - @XmlList - @XmlMap - @XmlKeyValuePairs - @XmlAttributeMap - @XmlElement - @XmlNamespace Hay consenso en que para trabajar con XML es estupendo -
@vicqr Eventos para mayor customización - serializer.pre_serialize - serializer.post_serialize -
serializer.pre_deserialize - serializer.post_deserialize -
@vicqr Eventos para mayor customización - serializer.pre_serialize - serializer.post_serialize -
serializer.pre_deserialize - serializer.post_deserialize Por ej., para utilizar un servicio (al que no puedes llamar desde la Entidad) http://knpuniversity.com/screencast/symfony-rest3/serialization-event-subscriber -
@vicqr Suscribers Los eventos tienen un getVisitor() El visitor es
el encargado del proceso de serialización Utilizando $visitor->addData() podemos añadir los campos que queramos -
@vicqr Suscribers Los eventos tienen un getVisitor() El visitor es
el encargado del proceso de serialización Utilizando $visitor->addData() podemos añadir los campos que queramos Esta forma de trabajar no encaja mucho con la filosofía del componente Serializer -
@vicqr Deserializando objetos complejos Utilizamos la anotación @Type para que
el serializer sepa qué objeto es cada uno -
@vicqr Algún detalle que viene bien saber - Hay que
solucionar el problema del underscore # app/config/config.yml parameters: jms_serializer.camel_case_naming_strategy.class: JMS\Serializer\Naming\IdenticalPropertyNamingStrategy -
@vicqr Algún detalle que viene bien saber - Hay que
solucionar el problema del underscore # app/config/config.yml parameters: jms_serializer.camel_case_naming_strategy.class: JMS\Serializer\Naming\IdenticalPropertyNamingStrategy https://knpuniversity.com/screencast/symfony-rest -
@vicqr Algún detalle que viene bien saber - Hay que
solucionar el problema del underscore # app/config/config.yml parameters: jms_serializer.camel_case_naming_strategy.class: JMS\Serializer\Naming\IdenticalPropertyNamingStrategy - Los campos null por defecto no se serializan # src/AppBundle/Controller/Api/SpaceshipController.php $context = new SerializationContext(); $context->setSerializeNull(true); return $this->container->get('jms_serializer')->serialize($data, $format, $context); https://knpuniversity.com/screencast/symfony-rest -
@vicqr Críticas y elogios frecuentes - Problema licencia - Mantenimiento
regular - Modelo “caja de herramientas” -
@vicqr Críticas y elogios frecuentes - Problema licencia - Mantenimiento
regular - Modelo “caja de herramientas” - Fácil de usar - Permite resolver problemas muy habituales muy cómodamente -
@vicqr Componente Serializer No pretende darte soluciones ya hechas para
todo Solo dos anotaciones (MaxDepth y Groups) Te proporciona algunos normalizadores muy útiles Va mucho de crear tus propios normalizadores Arquitectura potente y flexible que te permite exportar tus estructuras PHP -
@vicqr Anotaciones MaxDepth - Detecta y limita el nivel de
serialización - Especialmente útil serializando árboles grandes -
@vicqr Anotaciones MaxDepth - Detecta y limita el nivel de
serialización - Especialmente útil serializando árboles grandes Groups - Para serializar distintos grupos de atributos de tus entidades - De hecho sirve para implementar cualquiera de los métodos de exclusión -
@vicqr Anotaciones - Groups class Spaceship { /** * @Groups({"show",
"list"}) */ public $name; /** * @Groups({"show"}) */ public $color; /** * @Groups({"show", "list"}) */ public $maxSpeed; } -
@vicqr Anotaciones - Groups class Spaceship { /** * @Groups({"show",
"list"}) */ public $name; /** * @Groups({"show"}) */ public $color; /** * @Groups({"show", "list"}) */ public $maxSpeed; } -
@vicqr Anotaciones - Groups $groups = ['groups' => ['list']]; $response
= new Response($serializer->serialize($spaceship, 'json', $groups), 201); $response->headers->set('Content-Type', 'application/json'); return $response; Así indicamos que queremos serializar el grupo show -
@vicqr Setup anotaciones use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory; use Doctrine\Common\Annotations\AnnotationReader; use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader; $classMetadataFactory
= new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); $normalizer = new ObjectNormalizer($classMetadataFactory); Hay que inicializar classMetadataFactory y pasárselo al normalizador -
@vicqr Habilitar cache de Doctrine # app/config/config_prod.yml framework: serializer: cache:
serializer.mapping.cache.apc El sistema de extracción de metadatos es lento, conviene mucho habilitar la cache cuando se utilizan anotaciones (como Groups) -
@vicqr Los principales normalizers disponibles ObjectNormalizer: - El más completo
- Utiliza el PropertyAccess Component para leer y escribir en el objeto - Normaliza a partir de los métodos get/set/has/remove del objeto -
@vicqr Los principales normalizers disponibles ObjectNormalizer: - El más completo
- Utiliza el PropertyAccess Component para leer y escribir en el objeto - Normaliza a partir de los métodos get/set/has/remove del objeto GetSetMethodNormalizer: - Más sencillo que el anterior - Normaliza llamando a los getters y setters del objeto -
@vicqr Los principales normalizers disponibles ObjectNormalizer: - El más completo
- Utiliza el PropertyAccess Component para leer y escribir en el objeto - Normaliza a partir de los métodos get/set/has/remove del objeto GetSetMethodNormalizer: - Más sencillo que el anterior - Normaliza llamando a los getters y setters del objeto PropertyNormalizer: - Normaliza leyendo directamente las propiedades del objeto -
@vicqr Los principales normalizers disponibles ObjectNormalizer: - El más completo
- Utiliza el PropertyAccess Component para leer y escribir en el objeto - Normaliza a partir de los métodos get/set/has/remove del objeto GetSetMethodNormalizer: - Más sencillo que el anterior - Normaliza llamando a los getters y setters del objeto PropertyNormalizer: - Normaliza leyendo directamente las propiedades del objeto Todos devuelven un mapeo entre valores y nombres de propiedades, la diferencia es cómo lo obtienen -
@vicqr Los principales normalizers disponibles ObjectNormalizer: - El más completo
- Utiliza el PropertyAccess Component para leer y escribir en el objeto - Normaliza a partir de los métodos get/set/has/remove del objeto GetSetMethodNormalizer: - Más sencillo que el anterior - Normaliza llamando a los getters y setters del objeto PropertyNormalizer: - Normaliza leyendo directamente las propiedades del objeto https://symfony.com/doc/current/components/serializer.html#normalizers Todos devuelven un mapeo entre valores y nombres de propiedades, la diferencia es cómo lo obtienen -
@vicqr ObjectNormalizer Es muy útil, pero también tiene un coste
Se apoya en el PropertyAccess Component Ha sido bastante optimizado y siguen optimizándolo -
@vicqr ObjectNormalizer Es muy útil, pero también tiene un coste
Se apoya en el PropertyAccess Component Ha sido bastante optimizado y siguen optimizándolo Si la performance importa, mejor utilizar GetSetMethodNormalizer, PropertyNormalizer o el tuyo propio -
@vicqr PropertyAccess Component http://symfony.com/doc/current/components/property_access.html Permite leer de y escribir en
un objeto de forma cómoda Bien mediante propiedades públicas, setters/getters, hassers/issers Es esto concretamente lo que están intentando optimizar más -
Utilizar el Symfony Serializer va mucho de crear tus propios
normalizadores -
@vicqr Creando tus normalizadores use Symfony\Component\Serializer\NameConverter\NameConverterInterface; class OrgPrefixNameConverter implements NameConverterInterface
{ public function normalize($propertyName) { return 'apolo18_'.$propertyName; } public function denormalize($propertyName) { // remove prefix_ prefix return 'apolo18_' === substr($propertyName, 0, 8) ? substr($propertyName, 8) : $propertyName; } } Por ejemplo, para serializar atributos con un nombre distinto -
@vicqr Algunas funcionalidades secundarias - setIgnoredAttributes - Boolean functions (is,
has) - Callbacks Para no serializar algunas propiedades Métodos cuyo resultado booleano se serializa Forma posible de formatear una propiedad -
@vicqr Algunas funcionalidades secundarias - setIgnoredAttributes - Boolean functions (is,
has) - Callbacks Para no serializar algunas propiedades Métodos cuyo resultado booleano se serializa Forma posible de formatear una propiedad Están bien, pero no son imprescindibles -
@vicqr Callbacks $callback = function ($logoPath) { return 'thumb_'.$logoPath; };
$normalizer->setCallbacks(array('logo' => $callback)); Para formatear una propiedad del objeto (por ejemplo, fechas) -
@vicqr Referencias circulares Si no haces nada verás un error
como este: A circular reference has been detected (configured limit: 1). (500 Internal Server Error) -
@vicqr Referencias circulares Si no haces nada verás un error
como este: $normalizer->setCircularReferenceLimit(1); Número de veces que tiene que serializar un objeto antes de considerarlo referencia circular A circular reference has been detected (configured limit: 1). (500 Internal Server Error) -
@vicqr Referencias circulares Si no haces nada verás un error
como este: $normalizer->setCircularReferenceLimit(1); $normalizer->setCircularReferenceHandler(function ($object) { return $object->getName(); }); Número de veces que tiene que serializar un objeto antes de considerarlo referencia circular En lugar de lanzar excepción, la referencia circular es gestionada por este callable A circular reference has been detected (configured limit: 1). (500 Internal Server Error) -
@vicqr PropertyInfo Component Extrae información de las propiedades de una
clase PHP por a partir de metadatos (de Doctrine, PHP Reflection, PHP doc., etc.) -
@vicqr Deserializando objetos complejos El Serializer utiliza el componente PropertyInfo
para esto Hay que pasarle un Extractor al normalizador -
@vicqr Deserializando objetos complejos El Serializer utiliza el componente PropertyInfo
para esto Hay que pasarle un Extractor al normalizador use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; $normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor()); -
@vicqr Deserializando objetos complejos El Serializer utiliza el componente PropertyInfo
para esto Hay que pasarle un Extractor al normalizador use Symfony\Component\PropertyInfo\Extractor\ReflectionExtractor; $normalizer = new ObjectNormalizer(null, null, null, new ReflectionExtractor()); El Extractor indica de qué modo hay que extraer la información, el PropertyInfo permite al normalizador conocer el tipo de lo que tiene que desnormalizar -
@vicqr Críticas y elogios frecuentes - Ciertas cosas muy comunes
son menos directas que en JMS - XML mal (pobre) -
¿Y por qué lo del XML no está “bien hecho”
en el Serializer? -
@vicqr ¿Por qué es pobre el XML? El XML puede
ser bastante más rico que el JSON (namespaces, xincludes, instrucciones de procesado)… -
@vicqr ¿Por qué es pobre el XML? El XML puede
ser bastante más rico que el JSON (namespaces, xincludes, instrucciones de procesado)… Añadir información de un formato a otro no encaja con la filosofía del Serializer -
«We want different representations of the same data structure without
enrichment during the serialization process» Kevin Dunglas https://github.com/symfony/symfony/issues/19330#issuecomment-233515204 -
@vicqr Críticas y elogios frecuentes - Ciertas cosas muy comunes
son menos directas que en JMS - XML mal (pobre) -
@vicqr Críticas y elogios frecuentes - Ciertas cosas muy comunes
son menos directas que en JMS - XML mal (pobre) - Flexible, fácil de extender - Potente, permite cubrir necesidades complejas de tu lógica de negocio - Bien mantenido -
@vicqr Ha ido mejorando y pretende seguir - Por ejemplo,
algunas mejoras recientes: - DateTime Normalizer - Data URI Normalizer - CSV y YAML encoders -
@vicqr Ha ido mejorando y pretende seguir - Por ejemplo,
algunas mejoras recientes: - DateTime Normalizer - Data URI Normalizer - CSV y YAML encoders - Conversación continua acerca de cómo mejorar: - http://github.com/symfony/symfony/issues/16179 - http://github.com/symfony/symfony/issues/19330 - http://github.com/symfony/symfony/pull/19374 -
@vicqr Ha ido mejorando y pretende seguir - Por ejemplo,
algunas mejoras recientes: - DateTime Normalizer - Data URI Normalizer - CSV y YAML encoders - Conversación continua acerca de cómo mejorar: - http://github.com/symfony/symfony/issues/16179 - http://github.com/symfony/symfony/issues/19330 - http://github.com/symfony/symfony/pull/19374 - API Platform lo usa -
@vicqr Ha ido mejorando y pretende seguir - Por ejemplo,
algunas mejoras recientes: - DateTime Normalizer - Data URI Normalizer - CSV y YAML encoders - Conversación continua acerca de cómo mejorar: - http://github.com/symfony/symfony/issues/16179 - http://github.com/symfony/symfony/issues/19330 - http://github.com/symfony/symfony/pull/19374 - API Platform lo usa Las perspectivas son muy buenas -
@vicqr Serialicemos nuestra misión espacial { id: 18, name: "Apolo18",
budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } Quiero llamarlo “mission” No quiero mostrarlo Quiero mostrarlo en el list Quiero añadir “thumb_” Solo en la versión 2 de la API Quiero que sea un campo más -
@vicqr Serialicemos nuestra misión espacial { id: 18, name: "Apolo18",
budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } { mission: "Apolo18", email: "[email protected]", logo: "thumb_logo.jpg", spaceship: "Orion" } En el show -
@vicqr Serialicemos nuestra misión espacial { id: 18, name: "Apolo18",
budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } { mission: "Apolo18", email: "[email protected]", logo: "thumb_logo.jpg", spaceship: "Orion" } En el list -
@vicqr Serialicemos nuestra misión espacial { id: 18, name: "Apolo18",
budget: "USD 6 billion", email: "[email protected]", logo: "logo.jpg", twitter: "apolo18", spaceship: { id: 29, name: "Orion" } } { mission: "Apolo18", email: "[email protected]", logo: "thumb_logo.jpg", twitter: "apolo18", spaceship: "Orion" } En la versión 2 -
@vicqr Serialicemos nuestra misión espacial - Renombrar propiedades - Exponer
solo las que quiero - Tener distintas representaciones - Modificar el valor de una propiedad - Tener distintas versiones de la API - Que spaceship sea solo un nombre -
@vicqr Con JMSSerializerBundle - Renombrar propiedades - Exponer solo las
que quiero - Tener distintas representaciones - Modificar el valor de una propiedad - Tener distintas versiones de la API - Que spaceship sea solo un nombre @SerializedName @ExclusionStrategy, etc. @Groups @Accesor @Until, @Since @VirtualProperty -
@vicqr Con JMSSerializerBundle - Renombrar propiedades - Exponer solo las
que quiero - Tener distintas representaciones - Modificar el valor de una propiedad - Tener distintas versiones de la API - Que spaceship sea solo un nombre @SerializedName @ExclusionStrategy, etc. @Groups @Accesor @Until, @Since @VirtualProperty No hay problema @Type - ¿Serializar objetos encadenados? - ¿Deserializar objetos encadenados? -
@vicqr Con Symfony Serializer - Renombrar propiedades - Exponer solo
las que quiero - Tener distintas representaciones - Modificar el valor de una propiedad - Tener distintas versiones de la API - Que spaceship sea solo un nombre Un getter nuevo @Groups @Groups Un getter nuevo @Groups Un getter nuevo -
@vicqr Con Symfony Serializer - Renombrar propiedades - Exponer solo
las que quiero - Tener distintas representaciones - Modificar el valor de una propiedad - Tener distintas versiones de la API - Que spaceship sea solo un nombre Un getter nuevo @Groups @Groups Un getter nuevo @Groups Un getter nuevo O escribir tus normalizers, cuando quieras -
@vicqr Con Symfony Serializer - Renombrar propiedades - Exponer solo
las que quiero - Tener distintas representaciones - Modificar el valor de una propiedad - Tener distintas versiones de la API - Que spaceship sea solo un nombre Un getter nuevo @Groups @Groups Un getter nuevo @Groups Un getter nuevo O escribir tus normalizers, cuando quieras Ojo referencias circulares Pasar Extractor - ¿Serializar objetos encadenados? - ¿Deserializar objetos encadenados? -
Más que verlo como “cuál es mejor” hay que entender
que son dos animales distintos https://www.flickr.com/photos/5of7/5531923725 -
@vicqr Síntesis de críticas y puntos fuertes Licencia Mantenimiento Flexibilidad
Curva aprendizaje Out-of-the-box XML JMSSERIALIZERBUNDLE SYMFONY SERIALIZER -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
Debería usar JMS -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
No ¿Quiero “marranear” en el proceso de serialización? Debería usar JMS -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
No ¿Quiero “marranear” en el proceso de serialización? Sí Conviene que use JMS Debería usar JMS -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
No ¿Las transformaciones que quiero hacer son bastante comunes? No ¿Quiero “marranear” en el proceso de serialización? Sí Conviene que use JMS Debería usar JMS -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
No ¿Las transformaciones que quiero hacer son bastante comunes? Sí Quizá sea más rápido usar JMS No ¿Quiero “marranear” en el proceso de serialización? Sí Conviene que use JMS Debería usar JMS -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
No ¿Las transformaciones que quiero hacer son bastante comunes? Sí Quizá sea más rápido usar JMS No ¿Quiero “marranear” en el proceso de serialización? Sí Conviene que use JMS Debería usar JMS No El componente Serializer me vendrá muy bien -
@vicqr ¿Y la performance? - Son parecidos - En el
Symfony Serializer lo más lento es el ObjectNormalizer - Aunque se ha hecho más rápido y sigue siendo optimizado -
@vicqr ¿Y la performance? - Son parecidos - En el
Symfony Serializer lo más lento es el ObjectNormalizer - Aunque se ha hecho más rápido y sigue siendo optimizado https://github.com/symfony/symfony/issues/16179 Discusión y comparativas -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
No ¿Las transformaciones que quiero hacer son bastante comunes? Sí Quizá sea más rápido usar JMS No ¿Quiero “marranear” en el proceso de serialización? Sí Conviene que use JMS Debería usar JMS No El componente Serializer me vendrá muy bien -
@vicqr Casos de uso ¿Quiero trabajar bastante con XML? Sí
No ¿Las transformaciones que quiero hacer son bastante comunes? Sí Quizá sea más rápido usar JMS No ¿Quiero “marranear” en el proceso de serialización? Sí Conviene que use JMS Debería usar JMS No El componente Serializer me vendrá muy bien ¿La performance importa mucho? Puedo usar el ObjectNormalizer Debería usar otro o hacerme el mío No Sí -
@vicqr Experiencia personal 1. Hace años prefería JMSSerializer 2. En
algún momento encontré algo que no supe cómo hacer y cambié 3. La primera impresión con SfSerializer fue que era costoso 4. A día de hoy prefiero SfSerializer 5. Si tengo que hacer algo rápido cojo JMSSerializer -
@vicqr Experiencia personal 1. Hace años prefería JMSSerializer 2. En
algún momento encontré algo que no supe cómo hacer y cambié 3. La primera impresión con SfSerializer fue que era costoso 4. A día de hoy prefiero SfSerializer 5. Si tengo que hacer algo rápido cojo JMSSerializer Por lo que he visto por ahí, es una evolución bastante común -
@vicqr Qué recordar El serializador nos facilita crear distintas representaciones
de los objetos Tanto el bundle como el componente son dos muy buenas herramientas Son DISTINTAS, están enfocadas diferente y son óptimas para distintos casos -
@vicqr Qué recordar El serializador nos facilita crear distintas representaciones
de los objetos Tanto el bundle como el componente son dos muy buenas herramientas Son DISTINTAS, están enfocadas diferente y son óptimas para distintos casos El JMSSerializer te da más “hecho” El componente Serializer te facilita más montar lo que necesites -
@vicqr Resumen en un comentario de Github Just saying, but
to me the Symfony's serializer does not especially suffer from a bad design, especially because its philosophy is far from the JMS serializer one. The idea of the Symfony's serializer is to keep things simple, not trying to answer every use-cases natively, but instead providing a great and simple architecture. Despite the fact it offers some great features, I'm almost never using the ObjectNormalizer and rather rely on custom normalizers (sometimes encoders) for each of my needs. Because when I'm writing code for my application, I know exactly what the output should be. I do not need something answering everything, just good and simple interfaces over it. This is way less brainfucking than a JMS Serializer, as soon as you're not trying to answer very generic needs. Now I can understand some of your concerns if you need to handle things in a very generic way, but to me there is nothing impossible with the current architecture regarding this. But anyway, I don't think the Symfony's serializer should try to replace entirely a JMS Serializer. Those are simply two different tools. Use or don't use them. https://github.com/symfony/symfony/issues/19330#issuecomment-233405451 -
¡Con el Serializer no puedo hacer XMLs decentes! ¡El JMS
es una basura! -
¡Con el Serializer no puedo hacer XMLs decentes! ¡El JMS
es una basura! ¡¿Para qué quieres normalizadores y encoders?! -
¡Con el Serializer no puedo hacer XMLs decentes! ¡El JMS
es una basura! ¡¿Para qué quieres normalizadores y encoders?! ¡Deshonras a tu familia utilizando ese código! -
Consultoría, formación y desarrollo @limenius @vicqr [email protected] ¡Gracias! Organizamos React
Alicante 28-30 Sep. 2017 @ReactAlicante