Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to configure Normalizer with injected SerializerInterface

Tags:

php

symfony

I know that I can use and configure e.g. the DateTimeNormalizer of Symfony's Serializer like that:

$serializer = new Serializer(array(new DateTimeNormalizer()));

$dateAsString = $serializer->normalize(
    new \DateTime('2016/01/01'),
    null,
    array(DateTimeNormalizer::FORMAT_KEY => 'Y/m')
);
// $dateAsString = '2016/01';

But if I want to use the Serializer without instantiation but with the "full Symfony app" Dependency injection instead like:

//class MyService...

public function __construct(SerializerInterface $serializer)
{
    $this->serializer = $serializer;
}

//function MyFunction()
{
 $json = $this->serializer->serialize($object, 'json');
}

it already comes with the most common Normalizers in a pre-ordered fashion.

Usually this is perfectly fine for my purpose.

But how could I e.g. configure the DateTimeNormalizer::FORMAT_KEY in the injection scenario without creating CustomNormalizers or lots of instantiating?

like image 640
LBA Avatar asked Nov 29 '25 08:11

LBA


2 Answers

I just finished a sprint of my project with a similar problem.

If you only want to format by default you DateTime object, you can add those lines in your config file:

services:
   serializer.normalizer.datetime:
       class: ‘Symfony\Component\Serializer\Normalizer\DateTimeNormalizer
       arguments
           -
               !php/const Symfony\Component\Serializer\Normalizer\DateTimeNormalizer::FORMAT_KEY: 'Y-m-d\TH:i:s.uP’
       tags:
           - { name: serializer.normalizer, priority: -910 }

But in case you want more flexibility and control over the serializer, i advise you to not modify config file but try this following method :

So for my needs, i finally create a MainController that extends AbstractController and each of my controllers extends MainController.

In the MainController I instanciate the serializer as property, so that you can access serializer already configured in the MainControlle by $this->serializer.

MainController :

class MainController extends AbstractController {
    protected $serializer;

    public function __construct() {
        $encoders = [new XmlEncoder(), new JsonEncoder()];
        $normalizers = [
            new DateTimeNormalizer([DateTimeNormalizer::FORMAT_KEY => "your date format"]),
            new ObjectNormalizer()
        ];

        $this->serializer = new Serializer($normalizers, $encoders);
    }
}

PostController :

class PostController extends MainController {

    /**
    * @Route("/posts")
    **/
    public function listPosts() {

        $e = new Entity();

        return $this->serializer->serialize($e, "json");
    }
}

EDIT : !! I forgot to say, be careful to the order you arrange normalizer in instanciation. As DateTime is an object, the serializer will try to normalize the DateTime object with ObjectNormalizer first if ObjectNormalizer is added first (which is used to normalize entities) and will return an empty array.

like image 151
Esteban MANSART Avatar answered Dec 01 '25 22:12

Esteban MANSART


Since symfony 5.3, you could add the context as a inline metadata.

use Symfony\Component\Serializer\Annotation as Serializer;
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;

class SomeClass
{
    /**
     * @Serializer\Context({ DateTimeNormalizer::FORMAT_KEY = 'Y-m-d' })
     */
    public \DateTime $date;

    // In PHP 8 applications you can use PHP attributes instead:
    #[Serializer\Context([DateTimeNormalizer::FORMAT_KEY => 'Y-m-d'])]
    public \DateTime $date;
}

Read more about it in the blog post:

https://symfony.com/blog/new-in-symfony-5-3-inlined-serialization-context

like image 31
gseidel Avatar answered Dec 01 '25 20:12

gseidel