JSON:API: relations set through the constructor reject spec resource linkage (`relationships.<name>.data`)
Les mainteneurs répondent en général sous 2 jours
Personne n'a encore pris cette issue.
Évaluation
- Difficulté
- 3/5
- Temps estimé
- 1-2 jours
- Accessibilité débutants
- 68/100
Piste de recherche
Start with JsonApi\Serializer\ItemNormalizerTrait::setAttributeValue(), which strips the data wrapper, and compare it with AbstractItemNormalizer::createConstructorArgument(), which calls createAndValidateAttributeValue() without that step. Then check denormalizeRelation() in the JSON:API serializer. Done when a constructor-argument relation accepts relationships.<name>.data for to-one (including null) and to-many, and the flat shape is rejected, with a test modelled on the Book/Author fixture.
Rédigé par le modèle d'indexation à partir du texte de l'issue.
Description
API Platform version(s) affected
4.4.4
Description
When a relation is set through a constructor argument, the JSON:API denormalizer does not accept the resource linkage shape of the spec (relationships.<name>.data).
- To-one with
{"data": {"type": ..., "id": ...}}or{"data": null}: 400 "Only resource linkage supported currently". - To-many with
{"data": [...]}: 400 "The type of the key "data" must be "int", "string" given." - To-one with the flat shape
{"type": ..., "id": ...}, which is not the spec shape: 201.
The same relation as a public property (no constructor) accepts the spec shape.
The cause looks like this: JsonApi\Serializer\ItemNormalizerTrait::setAttributeValue() removes the data wrapper, but AbstractItemNormalizer::createConstructorArgument() calls createAndValidateAttributeValue() directly. The JSON:API normalizer does not override it, so the value still has the data wrapper when it gets to denormalizeRelation().
How to reproduce
#[ApiResource(operations: [new Get(provider: [self::class, 'provide'])])]
final class Author
{
public function __construct(public int $id = 1, public string $name = 'Frank Herbert') {}
public static function provide(): self { return new self(); }
}
#[ApiResource(operations: [
new Get(provider: [self::class, 'provide']),
new Post(processor: [self::class, 'process']),
])]
final class Book
{
/** @param list<Author> $coAuthors */
public function __construct(
public int $id = 1,
public string $title = 'Dune',
public ?Author $author = null,
public array $coAuthors = [],
) {}
public static function provide(): ?self { return null; }
public static function process(self $data, Operation $operation): self { return $data; }
}
// Same relation, set as a property
#[ApiResource(operations: [
new Get(provider: [self::class, 'provide']),
new Post(processor: [self::class, 'process']),
])]
final class Novel
{
public int $id = 1;
public string $title = 'Dune';
public ?Author $author = null;
public static function provide(): self { return new self(); }
public static function process(self $data, Operation $operation): self { return $data; }
}
POST |
relationships |
Status |
|---|---|---|
/books |
{"author":{"data":{"type":"Author","id":"/authors/1"}}} |
400 |
/books |
{"author":{"data":null}} |
400 |
/books |
{"coAuthors":{"data":[{"type":"Author","id":"/authors/1"}]}} |
400 |
/books |
{"author":{"type":"Author","id":"/authors/1"}} (not spec) |
201 |
/novels |
{"author":{"data":{"type":"Author","id":"/authors/1"}}} |
201 |
Full request for the first row:
POST /books
{"data":{"type":"Book","attributes":{"title":"A"},"relationships":{"author":{"data":{"type":"Author","id":"/authors/1"}}}}}
{"errors":[{"id":"/errors/400","title":"An error occurred","detail":"Only resource linkage supported currently, see: http://jsonapi.org/format/#document-resource-object-linkage.","status":400,"type":"/errors/400","links":{"type":"/errors/400"},"code":"400"}]}
Expected: a relation set through the constructor accepts the same relationships.<name>.data shape as a relation set through a property. The flat shape is not accepted.
Possible Solution
Remove the data wrapper for constructor arguments too, for example with a createConstructorArgument() override in ItemNormalizerTrait that does what setAttributeValue() does.
Additional Context
Checked on api-platform/core v4.4.4, Symfony 7.4, PHP 8.5, in an empty Symfony app. The related code in src/JsonApi/Serializer is the same on main, except for the error status cast (#8366).
The app has only FrameworkBundle, TwigBundle and ApiPlatformBundle, with this config:
$c->extension('api_platform', [
'mapping' => ['paths' => ['%kernel.project_dir%/src/ApiResource']],
'formats' => ['jsonapi' => ['application/vnd.api+json']],
'error_formats' => ['jsonapi' => ['application/vnd.api+json'], 'jsonproblem' => ['application/problem+json']],
'doctrine' => ['enabled' => false],
]);
All requests send Accept: application/vnd.api+json and, when they have a body, Content-Type: application/vnd.api+json. The responses are from the prod environment.
- Langage dominant
- PHP
- Étoiles
- 2.6k
- Forks
- 988
- Merge moyen
- 4 j 10 h
- PR mergées (30 j)
- 109
Préparer son environnement
- Aucun Dockerfile ni fichier Docker Compose
- Propose un modèle de pull request
- Lire le guide de contribution
Par où commencer
- Lisez l'issue en entier, puis le guide de contribution du projet.
- Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
- Forkez le dépôt et travaillez sur une branche.
- Ouvrez une pull request qui référence le numéro de l'issue.
Autres issues de api-platform/core
-
API Platform ignores array shapes in the JSON SchemaPeut-être pris @afraca l’a pris il y a 12 jours. Ouverte
Difficulté 2/5 1-3 heures Accessibilité débutants 78/100
api-platform/core#8766 ·
Les mainteneurs répondent en général sous 2 jours
-
Difficulté 2/5 1-3 heures Accessibilité débutants 72/100
api-platform/core#8744 ·
Les mainteneurs répondent en général sous 2 jours
-
JSON:API: resource schemas (`*.jsonapi`) have `properties` but no `type: object`Peut-être pris Une pull request liée à cette issue est ouverte ou déjà fusionnée. Ouverte
Difficulté 2/5 Moins d'une heure Accessibilité débutants 80/100
api-platform/core#8701 ·
Les mainteneurs répondent en général sous 2 jours
-
Difficulté 4/5 3-5 jours Accessibilité débutants 55/100
api-platform/core#8816 ·
Les mainteneurs répondent en général sous 2 jours
-
Difficulté 3/5 1-2 jours Accessibilité débutants 66/100
api-platform/core#8815 · 1 commentaire ·
Les mainteneurs répondent en général sous 2 jours
Toutes les issues de api-platform/core
Issues similaires
-
Difficulté 1/5 Moins d'une heure Accessibilité débutants 79/100
smarty-php/smarty#1220 ·
-
IDS: enable Suricata 8 Hyperscan MPM cache (detect.sgh-mpm-caching) in suricata.yaml templateOuverte
Difficulté 2/5 1-3 heures Accessibilité débutants 69/100
opnsense/core#11171 · 1 réaction ·
Les mainteneurs répondent en général sous 1 jour
-
Difficulté 2/5 1-3 heures Accessibilité débutants 68/100
getgrav/grav-plugin-form#664 ·
-
claude
Difficulté 2/5 1-3 heures Accessibilité débutants 82/100
sprain/sprainocchio#26 ·
-
sync-en
Difficulté 1/5 1-3 heures Accessibilité débutants 84/100
Les mainteneurs répondent en général sous 1 jour