dunglas / doctrine-json-odm

An object document mapper for Doctrine ORM using JSON types of modern RDBMS.
https://dunglas.fr
MIT License
579 stars 64 forks source link

Can't get to work with DateTime denormalization #85

Open ghostika opened 4 years ago

ghostika commented 4 years ago

I've used previously the 0.1.3 version and wanted to update it to v1. I've done the setup according to the documentation, just that 3 normalizer, but somehow my dateTime is not deserialized. I've checked it with xdebug and all 3 normalizers are registered and they have the right order.

My json string:

[{"#type": "App\\Entity\\EventSchedule", "endDate": "2020-04-11T00:00:00+02:00", "startDate": "2020-01-07T00:00:00+01:00"}]

EventSchedule

class EventSchedule
{
    /**
     * Start date.
     *
     * @var DateTimeImmutable
     */
    private $startDate;

    /**
     * End date.
     *
     * @var DateTimeImmutable
     */
    private $endDate;
}

Error message: Failed to denormalize attribute "endDate" value for class "App\Entity\EventSchedule": Expected argument of type "DateTimeImmutable", "string" given at property path "endDate"

I would highly appreciate any help. I have ApiPlatform installed, but in this request, it's not used. I tried just calling the service in the controller and passing the string to the deserialize event.

Toflar commented 4 years ago

Did you check the FAQ? https://github.com/dunglas/doctrine-json-odm#faq

ghostika commented 4 years ago

Yes, that's what I meant with documentation. And the 3 normalizers, what are under the FAQ are registered.

Toflar commented 4 years ago

Okay. So if you're using xdebug you should see if the datetime normalizer is called, right?

ghostika commented 4 years ago

Yes, and that's the problem that it's not called but the DateTimeNormalizer is one of the 3 normalizers and I can't figure it out why.

ghostika commented 4 years ago

I've taken a look at it again and I will try to create tomorrow an example SF app for this. What I've found with XDebug that it finds the DateTimeDenormalizer and when it calls in the serializer the dernormalize methode, it calls the denormalize methode from Dunglas Serializer. As the data is 2020-04-11T00:00:00+02:00 it skips the first if and not iterable, so skips the 2nd if and just simply returns the data, without a parent::denormalize call.

ghostika commented 4 years ago

Ok, I know what the problem is and it's connected to my previous comment. I've found no update.MD file so I thought the old serialized data should be comatible with the new. I did a serialization and that is the result:

[{"#type": "App\\Entity\\Event\\EventSchedule", "endDate":{"#type":"DateTimeImmutable","#scalar":"2020-04-11T00:00:00+02:00"}, "startDate": {"#type":"DateTimeImmutable","#scalar":"2020-01-07T00:00:00+01:00"}}]

instead of the result in the DB with version 0.1.3

[{"#type": "App\\Entity\\EventSchedule", "endDate": "2020-04-11T00:00:00+02:00", "startDate": "2020-01-07T00:00:00+01:00"}]

I think this should be documented somewhere.

ghostika commented 4 years ago

Or am I doing something wrong here @Toflar ?

TNAJanssen commented 4 years ago

You can always use the compiler pass to add all the normalizers & encoders:

$container->addCompilerPass(new SerializerPass('dunglas_doctrine_json_odm.serializer'));

TNAJanssen commented 4 years ago

@dunglas i changed the Symfony SerializerPass to work with your code, this will prevent these types of issues:

<?php

namespace App\Shared\Infrastructure\Symfony\Serializer;

use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\DependencyInjection\Reference;

class SerializerPass implements CompilerPassInterface
{
    use PriorityTaggedServiceTrait;

    private string $serializerService;
    private string $normalizerTag;
    private string $encoderTag;

    public function __construct(string $serializerService = 'dunglas_doctrine_json_odm.serializer', string $normalizerTag = 'serializer.normalizer', string $encoderTag = 'serializer.encoder')
    {
        $this->serializerService = $serializerService;
        $this->normalizerTag = $normalizerTag;
        $this->encoderTag = $encoderTag;
    }

    public function process(ContainerBuilder $container): void
    {
        if (!$container->hasDefinition($this->serializerService)) {
            return;
        }

        if (!$normalizers = $this->findAndSortTaggedServices($this->normalizerTag, $container)) {
            throw new RuntimeException(sprintf('You must tag at least one service as "%s" to use the "%s" service.', $this->normalizerTag, $this->serializerService));
        }

        array_unshift($normalizers, new Reference('dunglas_doctrine_json_odm.normalizer.array'));
        $normalizers[] = new Reference('dunglas_doctrine_json_odm.normalizer.object');

        $serializerDefinition = $container->getDefinition($this->serializerService);
        $serializerDefinition->replaceArgument(0, $normalizers);

        if (!$encoders = $this->findAndSortTaggedServices($this->encoderTag, $container)) {
            throw new RuntimeException(sprintf('You must tag at least one service as "%s" to use the "%s" service.', $this->encoderTag, $this->serializerService));
        }

        $serializerDefinition->replaceArgument(1, $encoders);
    }
}
ghostika commented 4 years ago

For me it was only 20 entries in the database, I just changed it with a script, but I think it would be good to mentions this difference.