Serialization Groups for Writing
Serialization Groups for Writing
~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
denormalizationContext is the COUNTERPART to chapter 23 – it controls WHICH fields a POST/PUT/PATCH request accepts AT ALL.
Adding denormalizationContext
#[ApiResource(
normalizationContext: ['groups' => ['project:read']],
denormalizationContext: ['groups' => ['project:write']]
)]
#[ORM\Entity(repositoryClass: ProjectRepository::class)]
class Project
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['project:read'])]
private ?int $id = null;
#[ORM\Column(length: 255)]
#[Assert\NotBlank]
#[Groups(['project:read', 'project:write'])]
private string $name = '';
#[ORM\Column(type: 'text', nullable: true)]
#[Groups(['project:read', 'project:write'])]
private ?string $description = null;
#[ORM\Column]
#[Groups(['project:read'])]
private \DateTimeImmutable $createdAt;
// ... rest unchanged
}id and createdAt belong ONLY to project:read – they must NOT be settable by the client (the id is assigned by the database, createdAt in the constructor). name and description belong to BOTH groups: readable AND writable.
Testing the behavior
curl -k -X POST https://localhost/api/projects \
-H 'Content-Type: application/json' \
-d '{"name": "New Project", "id": 999, "createdAt": "1970-01-01T00:00:00+00:00"}'id and createdAt in the request body get SILENTLY IGNORED – NO error, but also NO effect, since they're NOT included in project:write. The database still assigns its OWN id, the constructor still sets its OWN createdAt.
Why not just use a readonly property?
PHP's own readonly keyword would trigger a FATAL error on ANY write attempt – serialization groups, by contrast, simply IGNORE the value SILENTLY, which is the DESIRED, more tolerant behavior for an API field (a client that accidentally sends id should NOT crash).
Tipp: project:write can be FURTHER split into project:create and project:update, in case creating and editing should differ in their allowed fields – for OUR project, the simpler shared group is currently enough.