Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony 2 denormalization of nested objects with custom denormalizers

Here I'm building Symfony SDK for REST API. Most of data are JSON objects with nested other JSON objects. Like here

{
    "id": "eng_pl",
    "name": "Premier League",
    "_links": {
        "self": {
            "href": "/tournaments/eng_pl"
        },
        "seasons": {
            "href": "/tournaments/eng_pl/seasons/"
        }
    },
    "coverage": {
        "id": "eng",
        "name": "England",
        "_links": {
            "self": {
                "href": "/territories/eng"
            }
        }
    }
}

Deserialization must produce an object equal to object produced by the code listed below:

$tournament = new Tournament();
$tournament->setId('eng_pl');
$tournament->setName('Premier League');

$coverage = new Territory();
$coverage->setId('eng');
$coverage->setName('England');

$tournament->setCoverage($coverage);

I'm using my own custom Denormalizers, below the fragment of code of denormalizer for Tournament objects:

class TournamentDenormalizer implements DenormalizerInterface
{

    /**
     * @inheritdoc
     */
    public function supportsDenormalization($object, $type, $format = null)
    {

        if ($type != Tournament::class) {
            return false;
        }
        return true;
    }

    /**
     * @inheritdoc
     * @return Tournament
     */
    public function denormalize($object, $class, $format = null, array $context = array())
    {
        $tournament = new Tournament();
        $tournament->setId($object->id);
        $tournament->setName($object->name);

        if (isset($object->coverage)) {
            /** @var Territory $coverage */
            $coverage = ???; //HOWTO how to implement Territory denormalization here???
            $tournament->setCoverage(
                $coverage
            );
        }
            return $tournament;
    }
}

The question is how should I access TerritoryDenormalizer inside TournamentDenormalizer? I see two options:

First one (I'm using now) is to add implements DenormalizerAwareInterface to signature of denormalizer class and rely on Symfony\Component\Serializer\Serializer class:

$serializer = new Symfony\Component\Serializer\Serializer(
    [
        new TournamentDenormalizer(),
        new TerritoryDenormalizer()
    ], [
        new Symfony\Component\Serializer\Encoder\JsonDecode()
    ]
);
$serializer->deserialize($json, Tournament::class, 'json');

So in TournamentDeserializer it will be like here:

if (isset($object->coverage)) {
        /** @var Territory $coverage */
        $coverage = $this->denormalizer->denormalize(
            $object->coverage,
            Territory::class,
            $format,
            $context
        );
        $tournament->setCoverage(
            $coverage
        );
    }
}

The second approach Is to inject necessary denormalizers explicitly

$tournamentDenormalizer = new TournamentDenormalizer();
$tournamentDenormalizer->setTerritoryDenormalizer(new TerritoryDenormalizer());

So in TournamentDeserializer it will be like here:

if (isset($object->coverage)) {
        /** @var Territory $coverage */
        $coverage = $this->territoryDenormalizer->denormalize(
            $object->coverage,
            Territory::class,
            $format,
            $context
        );
        $tournament->setCoverage(
            $coverage
        );
    }
}

Which of approaches is the best? What alternative approaches are possible?

like image 568
Murat Erkenov Avatar asked Dec 16 '16 08:12

Murat Erkenov


1 Answers

Making your normalizer implement NormalizerAwareInterface (and eventually use NormalizerAwareTrait) is the way to go, this interface has been introduced for this specific use case.

like image 72
chalasr Avatar answered Oct 16 '22 19:10

chalasr