[GraphQL] Nested input with an `id` creates a new object instead of updating the existing one (regression in 4.4)
Los mantenedores suelen responder en 2 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 3/5
- Tiempo estimado
- 1-2 días
- Aptitud para principiantes
- 45/100
Línea de trabajo
Start with the GraphQL denormalizer class ApiPlatform\GraphQl\Serializer\ItemDenormalizer, which extends AbstractItemNormalizer without the trait, and compare it with the generic ApiPlatform\Serializer\ItemDenormalizer and ItemNormalizerTrait::denormalize(), which handles a nested id through OBJECT_TO_POPULATE. Check MutationTest for existing nested-input coverage. Done when a GraphQL update mutation with a nested id updates the existing child instead of creating a new one, with a functional test covering it.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
API Platform version(s) affected: 4.4.0 – 4.4.6, 5.0.5 (works in 4.3.21)
Description
A GraphQL update mutation that sends a nested (writable-link) relation with its id no longer updates the referenced object. API Platform instead denormalizes the nested input into a new object and ignores the id. For a to-many relation with orphanRemoval, this deletes the existing row and inserts a new one with a different identifier. If the new object misses required fields, the flush fails with a not-null violation.
This worked up to 4.3.x. There, the GraphQL ItemNormalizer extended ApiPlatform\Serializer\ItemNormalizer, whose denormalize() turned a nested id into OBJECT_TO_POPULATE. Since the normalizer/denormalizer split in #7713, GraphQL denormalization goes through ApiPlatform\GraphQl\Serializer\ItemDenormalizer. That class extends AbstractItemNormalizer directly and does not use ApiPlatform\Serializer\ItemNormalizerTrait, which still contains that logic (denormalize() / updateObjectToPopulate()). The REST and JSON-LD denormalizers still use their traits.
The 4.4.0 changelog lists #7713 only as a refactoring, without a behaviour change. The serialization docs ("Embedded relations": an embedded resource with an @id is retrieved and updated, one without is created) describe the same rule for JSON-LD. GraphQL followed it with id up to 4.3. I could not find a GraphQL functional test that sends a nested id on an update mutation. MutationTest only covers nested inputs without an id, which are created.
How to reproduce
Fresh symfony/skeleton 8.1, api-platform/symfony, api-platform/doctrine-orm and api-platform/graphql (all split packages pinned to the same version), SQLite, api_platform.graphql.enabled: true.
#[ORM\Entity]
#[ApiResource(normalizationContext: ['groups' => ['owner:read']], denormalizationContext: ['groups' => ['owner:write']])]
class Owner
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column] #[Groups(['owner:read'])]
private ?int $id = null;
#[ORM\Column] #[Groups(['owner:read', 'owner:write'])]
private string $name = '';
#[ORM\OneToMany(mappedBy: 'owner', targetEntity: Child::class, cascade: ['persist'], orphanRemoval: true)]
#[Groups(['owner:read', 'owner:write'])]
private Collection $children;
// constructor, getters, addChild()/removeChild() maintaining Child::$owner
}
#[ORM\Entity]
#[ApiResource]
class Child
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column] #[Groups(['owner:read'])]
private ?int $id = null;
#[ORM\Column] #[Groups(['owner:read', 'owner:write'])]
private string $name = '';
#[ORM\ManyToOne(inversedBy: 'children')]
private ?Owner $owner = null;
// getters/setters
}
Seed one Owner with one Child (/api/children/1, name before), then run:
mutation($input: updateOwnerInput!) {
updateOwner(input: $input) { owner { id children { edges { node { id name } } } } }
}
{ "input": { "id": "/api/owners/1", "children": [ { "id": "/api/children/1", "name": "after" } ] } }
Result (same app; only the API Platform version changes):
| version | response child | child table after the mutation |
|---|---|---|
| 4.3.21 | /api/children/1, after |
[{"id":1,"name":"after","owner_id":1}] |
| 4.4.6 | /api/children/2, after |
[{"id":2,"name":"after","owner_id":1}] |
| 5.0.5 | /api/children/2, after |
[{"id":2,"name":"after","owner_id":1}] |
Expected: the existing child /api/children/1 is updated, as in 4.3.
Possible Solution
Let ApiPlatform\GraphQl\Serializer\ItemDenormalizer use ApiPlatform\Serializer\ItemNormalizerTrait, as the generic ApiPlatform\Serializer\ItemDenormalizer does, so that a nested id is resolved through the IRI converter into OBJECT_TO_POPULATE again, including the api_allow_update check. A functional test that updates a nested relation by id in a GraphQL mutation would cover it.
Additional Context
Workaround: decorate api_platform.graphql.denormalizer.item and set OBJECT_TO_POPULATE from the nested id before delegating, mirroring ItemNormalizerTrait::denormalize().
Environment of the reproduction: PHP 8.4.27 (CLI), macOS 26.6, Symfony 8.1.10, Doctrine ORM 3.7.6, webonyx/graphql-php 15.39.0, SQLite. Only the api-platform/* versions differ between the three runs.
- Lenguaje dominante
- PHP
- Estrellas
- 2.6k
- Forks
- 988
- Merge medio
- 4 d 10 h
- PR fusionados (30 d)
- 92
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de api-platform/core
-
API Platform ignores array shapes in the JSON SchemaPosiblemente ocupada @afraca la tomó hace 9 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
api-platform/core#8766 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
api-platform/core#8744 ·
Los mantenedores suelen responder en 2 días
-
JSON:API: resource schemas (`*.jsonapi`) have `properties` but no `type: object`Posiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 Menos de una hora Aptitud para principiantes 80/100
api-platform/core#8701 ·
Los mantenedores suelen responder en 2 días
-
Dificultad 5/5 Más de una semana Aptitud para principiantes 30/100
api-platform/core#8791 ·
Los mantenedores suelen responder en 2 días
-
Preserve exact SearchFilter behaviour for Doctrine collection associations with QueryParameterAbierto
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
api-platform/core#8790 ·
Los mantenedores suelen responder en 2 días
Todos los issues de api-platform/core
Issues similares
-
bug ui
Dificultad 2/5 1-3 horas Aptitud para principiantes 62/100
hashtopolis/server#2614 ·
Los mantenedores suelen responder en 1 día
-
sync-en
Dificultad 1/5 1-3 horas Aptitud para principiantes 84/100
Los mantenedores suelen responder en 1 día
-
sync-en
Dificultad 1/5 Menos de una hora Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
Перевод устарел
Dificultad 1/5 Menos de una hora Aptitud para principiantes 62/100
-
bug-report bug-report-triage
Dificultad 2/5 1-3 horas Aptitud para principiantes 69/100
Codeinwp/wp-maintenance-mode#596 · 1 comentario ·
Los mantenedores suelen responder en 1 día