JSON:API: relations set through the constructor reject spec resource linkage (`relationships.<name>.data`)
维护者通常 1 天内回复
还没有人认领这个 Issue。
评估
调研方向
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.
由索引模型根据 Issue 内容生成。
描述
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.
- 主要语言
- PHP
- 星标
- 2.6k
- 派生
- 989
- 平均合并
- 1 天 23 小时
- 30 天内合并 PR
- 102
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
api-platform/core 的其他 Issue
-
JSON:API: resource schemas (`*.jsonapi`) have `properties` but no `type: object`可能已有人在做 @ousamabenyounes 于 1 天前认领。 未关闭
难度 2/5 1 小时以内 新手友好度 80/100
api-platform/core#8701 ·
维护者通常 1 天内回复
-
JSON:API: `JsonApiCollectionBaseSchemaNoPagination` declares `data` as `type: array` without `items`未关闭
难度 2/5 1-3 小时 新手友好度 82/100
api-platform/core#8699 ·
维护者通常 1 天内回复
-
api:openapi:export --spec-version=3.0.0 outputs `webhooks`, which is invalid in OpenAPI 3.0可能已有人在做 关联的 PR 仍在进行中或已合并。 未关闭
难度 2/5 1-3 小时 新手友好度 72/100
api-platform/core#8690 ·
维护者通常 1 天内回复
-
难度 1/5 1 小时以内 新手友好度 85/100
api-platform/core#8680 ·
维护者通常 1 天内回复
-
难度 3/5 1-2 天 新手友好度 68/100
api-platform/core#8725 ·
维护者通常 1 天内回复
查看 api-platform/core 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 65/100
维护者通常 1 天内回复
-
needs triage
难度 2/5 1-3 小时 新手友好度 66/100
-
bug
难度 2/5 1-3 小时 新手友好度 88/100
-
难度 2/5 1-3 小时 新手友好度 88/100
grokability/snipe-it#19857 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 92/100
维护者通常 1 天内回复