New in Symfony 8.2 (in development). Serialization groups choose which properties are output; they said nothing about what they are called. An API that gets a new version while keeping the old one therefore ended up with a second class, or with a loop that renames the keys by hand after the serializer.
One name per group
#[SerializedName] and #[SerializedPath] become repeatable and accept a groups argument:
#[Groups(['v1', 'v2'])] #[SerializedName('product_name', groups: 'v1')] public string $name;
Serialized with the v1 group, the property comes out as product_name; with v2, as name. A name declared without a group applies to all of them, and serves as a fallback. It also works the other way, on deserialization.
Leaving a group out
The new AbstractNormalizer::IGNORED_GROUPS ('ignored_groups') context removes the properties of a group, even if they also belong to a requested group:
$normalizer->normalize($books, 'json', [ AbstractNormalizer::GROUPS => 'v2', AbstractNormalizer::IGNORED_GROUPS => 'internal', ]);
Unlike #[Ignore], which removes a property everywhere, it only applies where you pass it. With a context builder, it's withIgnoredGroups().
Your task
The bookshop serves its books in three places: the v1 API, which partners still read, the v2 API, and the back office. LivresController makes up by hand for what the groups can't express: it renames the v1 keys in a loop, and removes the stock from v2 with unset().
- In
Livre, declare the v1 names (name, and a flatprice_cents) for thev1group only, without changing what v2 and the back office see. - In
LivresController, make v1 a plain serialization of thev1group, and leave theinternalgroup out of v2 through the context.
The three requests in the “Requests” tab must respond exactly as before.