When working with objects that are pretty similar or share lots of properties, is common to use interfaces or abstract classes. The problem with classes that extend from other abstract classes is that the Serializer component doesn't know how to serialize/deserialize them correctly.
In Symfony 4.1, we improved the Serializer component to support this feature
using a "discriminator class mapping". Consider an application that defines an
abstract CodeRepository
class extended by GitHubCodeRepository
and
BitBucketCodeRepository
classes. This example shows how to serialize and
deserialize those objects when using the stand-alone Serializer component:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
// ...
$discriminator = new ClassDiscriminatorResolver();
// $type is the property used to differentiate between classes
$discriminator->addClassMapping(
CodeRepository::class,
new ClassDiscriminatorMapping('type', [
'github' => GitHubCodeRepository::class,
'bitbucket' => BitBucketCodeRepository::class,
])
);
// pass the discriminator as the last argument of the normalizer
$serializer = new Serializer(
array(new ObjectNormalizer(null, null, null, null, $discriminator)),
array('json' => new JsonEncoder())
);
// when serializing, the discriminator knows that the value of the "type"
// property for GitHubCodeRepository classes must be set to {"type": "github"}
$serialized = $serializer->serialize(new GitHubCodeRepository());
// when deserializing into the abstract CodeRepository class, the discriminator
// uses the "type" value to know that $repository must be an instance of GitHubCodeRepository
$repository = $serializer->unserialize($serialized, CodeRepository::class, 'json');
When using this feature in a Symfony application, you can simply configure all this in a YAML or XML file and even using PHP annotations:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
namespace App\Repository;
use Symfony\Component\Serializer\Annotation\DiscriminatorMap;
/**
* @DiscriminatorMap(typeProperty="type", mapping={
* "github"="App\Repository\GitHubCodeRepository",
* "bitbucket"="App\Repository\BitBucketCodeRepository"
* })
*/
interface CodeRepository
{
// ...
}
You can read more about this new feature in the Serializing Interfaces and Abstract Classes section of the Serializer Component documentation.
Nice ! There is a little typo : using a "discrimator class mapping" (should be discriminator)
@Grégoire fixed! Thanks.
Developed my own annotation for this in december'17. Kind of funny and disappointing :D
@Timo Förster: less code for you to maintain, celebrate it :-)
new ObjectNormalizer(null, null, null, null, $discriminator)
Do you feel the smell?