Nuevo en Symfony 4.1: mejoras relacionadas con el Serializer

Añadido un ConstraintViolationListNormalizer

Cuando se trabajan con APIs en aplicaciones Symfony, es habitual utilizar un código como el siguiente:

/**
 * @Route("/blog/new", name="api_blog_new")
 * @Method("POST")
 * @Security("is_granted('ROLE_ADMIN')")
 */
public function new(Request $request, SerializerInterface $serializer, ValidatorInterface $validator)
{
    $data = $request->getContent();
    $post = $serializer->deserialize($data, Post::class, 'json', ['groups' => ['post_write']]);
    $post->setAuthor($this->getUser());
 
    $violations = $validator->validate($post);
    if (count($violations) > 0) {
        $repr = $serializer->serialize($violations, 'json');
 
        return JsonResponse::fromJsonString($repr, 400);
    }
 
    // ...
}

La variable $violations contiene un objeto de tipo ConstraintViolationList y es habitual transformarlo en una lista de errores que se serializa e incluye en la respuesta JSON. Por eso en Symfony 4.1 hemos añadido una clase ConstraintViolationListNormalizer que hace eso por ti automáticamente. El normalizador sigue el estándar RFC 7807 para generar la lista de errores.

Esta funcionalidad fue contribuida por Grégoire Pineau en el pull request #22150.

Obtener los resultados XML y CSV como colecciones

Los encoders CsvEncoder y XmlEncoder ahora definen una nueva opción de configuración llamada as_collection. Si pasas esa opción a true como parte del contexto de los encoders, los resultados se devuelven como una colección.

Esta funcionalidad fue contribuida por Hamza Amrouche en los pull requests #25218 y #25369.

Argumentos por defecto para la denormalización

Si el constructor de una clase define argumentos, tal y como sucede habitualmente cuando se utilizan value objects, el serializar no es capaz de construir el objeto. En Symfony 4.1 hemos añadido una opción llamada default_constructor_arguments para solucionar este problema.

En el siguiente ejemplo, tanto foo como bar son requeridos por el constructor, pero solo se define explícitamente el valor de foo. El valor de bar se obtiene mediante la opción default_constructor_arguments:

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
 
class MyObj
{
    private $foo;
    private $bar;
 
    public function __construct($foo, $bar)
    {
        $this->foo = $foo;
        $this->bar = $bar;
    }
}
 
$normalizer = new ObjectNormalizer($classMetadataFactory);
$serializer = new Serializer(array($normalizer));
 
// equivalente a $data = new MyObj('Hello', '');
$data = $serializer->denormalize(['foo' => 'Hello'], 'MyObj', [
    'default_constructor_arguments' => [
        'MyObj' => ['foo' => '', 'bar' => ''],
    ]
]);

Esta funcionalidad fue contribuida por Maxime Veber en el pull request #25493.

Añadido un handler para MaxDepth

En ocasiones, en vez de parar la serialización cuando se alcanza el máximo nivel de anidamiento configurado, es mejor dejar que sea la aplicación la que decida cómo gestionar esta situación.

En Symfony 4.1 puedes resolver este problema definiendo un handler con el método setMaxDepthHandler():

use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Annotation\MaxDepth;
use Symfony\Component\Serializer\Mapping\Factory\ClassMetadataFactory;
use Symfony\Component\Serializer\Mapping\Loader\AnnotationLoader;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
 
class Foo
{
    public $id;
 
    /** @MaxDepth(1) */
    public $child;
}
 
$level1 = new Foo();
$level1->id = 1;
 
$level2 = new Foo();
$level2->id = 2;
$level1->child = $level2;
 
$level3 = new Foo();
$level3->id = 3;
$level2->child = $level3;
 
$classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
$normalizer = new ObjectNormalizer($classMetadataFactory);
$normalizer->setMaxDepthHandler(function ($foo) {
    return '/foos/'.$foo->id;
});
 
$serializer = new Serializer(array($normalizer));
$result = $serializer->normalize($level1, null, array(ObjectNormalizer::ENABLE_MAX_DEPTH => true));
/*
$result = array[
    'id' => 1,
    'child' => [
        'id' => 2,
        'child' => '/foos/3',
    ]
];
*/

Esta funcionalidad fue contribuida por Kévin Dunglas en el pull request #26108.

Ignorar los comentarios cuando se decodifica XML

En las versiones anteriores de Symfony, los comentarios XML se procesaban al decodificar los contenidos. En Symfony 4.1, los comentarios XML se eliminan por defecto, pero puedes controlar este comportamiento con un nuevo argumento del constructor:

class XmlEncoder
{
    public function __construct(
        string $rootNodeName = 'response',
        int $loadOptions = null,
        array $ignoredNodeTypes = array(XML_PI_NODE, XML_COMMENT_NODE)
    ) {
        // ...
    }
}

Esta funcionalidad fue contribuida por James Sansbury en el pull request #26445.

Fuente: New in Symfony 4.1: Serializer improvements

Comentarios

Publicada el

29 de mayo de 2018

Etiquetas

Proyectos Symfony destacados

La forma más sencilla de generar el backend de tus aplicaciones Symfony. Ver más

Síguenos en @symfony_es para acceder a las últimas noticias.