feat(jsonschema): add support for normalization/denormalization with attributes#7629
Conversation
|
This looks nice, can you add a test on the JsonSchema generation (tests/Functional/JsonSchema/JsonSchemaTest.php) as I'm not sure that we'd gather the correct properties. |
cda5443 to
36c1db8
Compare
|
I rebased onto 4.2 to retrieve the code that fixes the 8 failing tests. |
d3a767a to
4691c25
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds support for using the attributes context option in normalization and denormalization, similar to how serialization groups currently work. This allows developers to control which properties are exposed in API responses and accepted in requests through the attributes context, providing an alternative to serialization groups for controlling API visibility.
Changes:
- Added support for attribute-based property filtering in normalization/denormalization contexts
- Updated schema generation to reflect attribute-based filtering in OpenAPI documentation
- Added comprehensive test coverage for the new attribute filtering functionality
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Metadata/Property/Factory/SerializerPropertyMetadataFactory.php | Added logic to extract and apply normalization/denormalization attributes when determining property read/write/link permissions |
| src/JsonSchema/SchemaFactory.php | Updated schema building to propagate attribute context to child properties and extract attributes from operation contexts |
| src/JsonSchema/DefinitionNameFactory.php | Extended definition name generation to include attributes in the naming scheme alongside groups |
| tests/Functional/JsonSchema/JsonSchemaTest.php | Added functional test verifying schema generation with attribute-based filtering |
| tests/Fixtures/TestBundle/ApiResource/ParentAttribute.php | New fixture class demonstrating parent resource with attribute-based filtering |
| tests/Fixtures/TestBundle/ApiResource/ChildAttribute.php | New fixture class demonstrating child resource in attribute filtering scenarios |
| src/Metadata/Tests/Property/Factory/SerializerPropertyMetadataFactoryTest.php | Added unit tests for attribute-based property filtering logic |
| src/JsonSchema/Tests/SchemaFactoryTest.php | Added unit tests for schema generation with serializer attributes |
| src/JsonSchema/Tests/Fixtures/ApiResource/ParentAttributeDummy.php | New test fixture for schema factory attribute testing |
| src/JsonSchema/Tests/Fixtures/ApiResource/ChildAttributeDummy.php | New test fixture for child resource in schema factory tests |
| src/JsonSchema/Tests/DefinitionNameFactoryTest.php | Added test cases for definition name generation with attributes |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| $parts[] = $key.'.'.$child; | ||
| } | ||
| } elseif (\is_string($key)) { | ||
| $parts[] = $key.'_'.$value; |
There was a problem hiding this comment.
When a string key exists with a string value (non-array), the logic concatenates key and value with an underscore. However, this appears to be treating the value as a property name, but it should likely just use the key. For example, if the input is ['author' => 'someValue'], this produces 'author_someValue' instead of just 'author'. This doesn't match the pattern used for array values (line 125) where only the key is used as a prefix.
| $parts[] = $key.'_'.$value; | |
| $parts[] = $key; |
97bbf89 to
8bc6e8d
Compare
|
I removed Since ExampleFor 'attributes' => ['title', 'name', 'author' => 'name']JSON result and api doc :{
"title": "The book title",
"name": "string",
"author": {
"id": 3,
"name": "string"
}
}Before (
|
8bc6e8d to
aa28771
Compare
|
One last thing I consider this a new feature, could you target main ? you can also squash your commits to a single one |
e4998a6 to
5b4e851
Compare
5b4e851 to
919c69e
Compare
|
Done @soyuka After targeting |
ApiResourceexamples:#[ApiResource( operations: [ new Get( normalizationContext: [ 'attributes' => ['title', 'name', 'author' => ['name']], ], ), new Post( denormalizationContext: [ 'attributes' => ['title', 'name', 'author' => ['name']], ], normalizationContext: [ 'attributes' => ['id', 'title', 'author' => ['id', 'name']], ], ) ] )] class Book { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] private ?int $id = null; #[Groups(['book:read'])] #[ORM\Column(type: 'string', length: 255)] public string $title = 'The book title'; #[ORM\Column(type: 'string', length: 255)] private ?string $name = null; #[ORM\ManyToOne(targetEntity: Author::class)] #[ORM\JoinColumn(nullable: false)] private Author $author; #[ORM\Column(type: 'string', length: 20)] public string $isbn = '978-3-16-148410-0'; // getters and setters }Before
API response
/api/books/id GET:API Docs
JSON Schema
bin/console api:openapi:export --output=swagger_docs.jsonNow
API response
/api/books/id GET:API Docs
JSON Schema