Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use (chain?) multiple normalizers with Symfony Serializer?

can somebody try to explain me how to use multiple normalizers when serializing data from multiple classes with the Symfony serializer?

Lets say that I have the following classes:

class User
{
    private $name;
    private $books;

    public function __construct()
    {
        $this->books = new ArrayCollection();
    }

    // getters and setters
}

class Book
{
    private $title;

    public function getTitle()
    {
        return $this->title;
    }    

    public function setTitle($title)
    {
        $this->title = $title;
    }
}

And I want to serialize an user who has multiple books.

$first = new Book();
$first->setTitle('First book');

$second = new Book();
$second->setTitle('Second book');

$user = new User();
$user->setName('Person name');
$user->addBook($first);
$user->addBook($second);

dump($this->get('serializer')->serialize($user, 'json'));
die();

Let's say that I also want to include a hash when serializing a book, so I have the following normalizer:

class BookNormalizer implements NormalizerInterface
{
    public function normalize($object, $format = null, array $context = array())
    {
        return [
            'title' => $object->getTitle(),
            'hash' => md5($object->getTitle())
        ];
    }

    public function supportsNormalization($data, $format = null)
    {
        return $data instanceof Book;
    }
}

And I am getting the expected result: {"name":"Person name","books":[{"title":"First book","hash":"a9c04245e768bc5bedd57ebd62a6309e"},{"title":"Second book","hash":"c431a001cb16a82a937579a50ea12e51"}]}

The problem comes when I also add a normalizer for the User class:

class UserNormalizer implements NormalizerInterface
{
    public function normalize($object, $format = null, array $context = array())
    {
        return [
            'name' => $object->getName(),
            'books' => $object->getBooks()
        ];
    }

    public function supportsNormalization($data, $format = null)
    {
        return $data instanceof User;
    }
}

Now, the books aren't normalized using the previously given normalizer, and i get the following: {"name":"Person name","books":[{},{}]}

I tried to find a way (documentation and other articles) to always call the normalizers for the given types (eg. always call the book normalizer when the type is Book, even if the data is nested and used in another normalizer) but could not succeed.

I think i have misunderstood something about normalizers but don't know what. Can somebody explain to is what i want possible and how to do it?

like image 538
Mario Avatar asked Sep 04 '17 16:09

Mario


People also ask

Does Symfony have a serialization component?

Symfony has a great serialization component! Symfony already addressed the problem of content serialization with the Serializer Component. It is not as ready to use as JMS Serializer but it is extendable and flexible. Quick reminder: In the Symfony serialization component, a serializer is composed of two halves:

What is a normalizer in the serializer component?

The Serializer component uses normalizers to transform any data into an array. The component provides several built-in normalizers but you may need to create your own normalizer to transform an unsupported data structure. Imagine you want add, modify, or remove some properties during the serialization process.

How do I enable multiple normalizers in Symfony?

Certain normalizers are enabled by default when using the Serializer component in a Symfony application, additional ones can be enabled by tagging them with serializer.normalizer. Here is an example of how to enable the built-in GetSetMethodNormalizer, a faster alternative to the ObjectNormalizer:

How to DENORMALIZE data in Symfony?

It is configured by default in Symfony applications with the Serializer component enabled. This normalizer reads the content of the class by calling the "getters" (public methods starting with "get"). It will denormalize data by calling the constructor and the "setters" (public methods starting with "set").


1 Answers

You have to use the NormalizerAwareTrait so you can access the normalizer for books

  • add interface
  • use trait
  • call normalize() method for books

code:

class UserNormalizer implements NormalizerInterface, NormalizerAwareInterface
{
    use NormalizerAwareTrait;

    public function normalize($object, $format = null, array $context = array())
    {
        return [
            'name' => $object->getName(),
            'books' => $this->normalizer->normalize($object->getBooks(), $format, $context)
        ];
    }

    public function supportsNormalization($data, $format = null)
    {
        return $data instanceof User;
    }
}
like image 178
ThomasK Avatar answered Oct 06 '22 11:10

ThomasK