Skip to content

Commit de4e4b3

Browse files
committed
feat: add support for normalization/denormalization with attributes
1 parent 8dc8c8d commit de4e4b3

File tree

1 file changed

+347
-1
lines changed

1 file changed

+347
-1
lines changed

core/serialization.md

Lines changed: 347 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ the specific configuration for this operation.
389389

390390
Refer to the [operations](operations.md) documentation to learn more.
391391

392-
## Embedding Relations
392+
## Embedding Relations With Serialization Groups
393393

394394
<p align="center" class="symfonycasts"><a href="https://symfonycasts.com/screencast/api-platform/relations?cid=apip"><img src="../symfony/images/symfonycasts-player.png" alt="Relations screencast"><br>Watch the Relations screencast</a></p>
395395

@@ -727,6 +727,352 @@ class PlainIdentifierDenormalizer implements DenormalizerInterface, Denormalizer
727727
}
728728
```
729729

730+
## Using Serialization Attributes
731+
732+
In addition to using serialization groups, you can specify which attributes (properties) of a
733+
resource should be exposed during normalization (read) and denormalization (write) processes. This
734+
is done through the `attributes` key in the serialization context.
735+
736+
It is simple to specify which attributes to use in the API system:
737+
738+
1. Add the normalization context and denormalization context to the resource, and specify which
739+
attributes to expose.
740+
741+
<code-selector>
742+
743+
```php
744+
<?php
745+
// api/src/ApiResource/Book.php with Symfony or app/ApiResource/Book.php with Laravel
746+
namespace App\ApiResource;
747+
748+
use ApiPlatform\Metadata\ApiResource;
749+
750+
#[ApiResource(
751+
normalizationContext: [
752+
'attributes' => ['id', 'title'],
753+
],
754+
denormalizationContext: [
755+
'attributes' => ['title'],
756+
],
757+
)]
758+
class Book
759+
{
760+
public ?int $id = null;
761+
762+
public string $title = '';
763+
764+
public string $isbn = '';
765+
766+
// ...
767+
}
768+
```
769+
770+
```yaml
771+
# The YAML syntax is only supported for Symfony
772+
# api/config/api_platform/resources/Book.yaml
773+
App\ApiResource\Book:
774+
normalizationContext:
775+
attributes: ["id", "title"]
776+
denormalizationContext:
777+
attributes: ["title"]
778+
```
779+
780+
```xml
781+
<!-- The XML syntax is only supported for Symfony -->
782+
<!-- api/config/api_platform/resources.xml -->
783+
<?xml version="1.0" encoding="UTF-8" ?>
784+
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
785+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
786+
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
787+
https://api-platform.com/schema/metadata/resources-3.0.xsd">
788+
<resource class="App\ApiResource\Book">
789+
<normalizationContext>
790+
<values>
791+
<value name="attributes">
792+
<values>
793+
<value>id</value>
794+
<value>title</value>
795+
</values>
796+
</value>
797+
</values>
798+
</normalizationContext>
799+
<denormalizationContext>
800+
<values>
801+
<value name="attributes">
802+
<values>
803+
<value>title</value>
804+
</values>
805+
</value>
806+
</values>
807+
</denormalizationContext>
808+
</resource>
809+
</resources>
810+
```
811+
812+
</code-selector>
813+
814+
In the previous example, the `id` and `title` properties will be visible when reading (`GET`) the
815+
object. When writing (`PUT` / `PATCH` / `POST`), only `title` is accepted. The `isbn` property is
816+
never exposed because it was not specified in the attributes list.
817+
818+
Internally, API Platform passes the value of the `normalizationContext` as the 3rd argument of
819+
[the `Serializer::serialize()` method](https://api.symfony.com/master/Symfony/Component/Serializer/SerializerInterface.html#method_serialize)
820+
during the normalization process. `denormalizationContext` is passed as the 4th argument of
821+
[the `Serializer::deserialize()` method](https://api.symfony.com/master/Symfony/Component/Serializer/SerializerInterface.html#method_deserialize)
822+
during denormalization (writing).
823+
824+
In addition to the `attributes` key, you can configure any Symfony Serializer option through the
825+
`$context` parameter (e.g. the `enable_max_depth` key when using
826+
[the `@MaxDepth` annotation](https://symfony.com/doc/current/components/serializer.html#handling-serialization-depth)).
827+
828+
Any attributes configuration that you specify will also be leveraged by the built-in actions and the
829+
OpenAPI documentation generator.
830+
831+
## Using Serialization Attributes per Operation
832+
833+
It is possible to specify normalization and denormalization contexts (as well as any other
834+
attribute) on a per-operation basis. API Platform will always use the most specific definition. For
835+
instance, if normalization attributes are set both at the resource level and at the operation level,
836+
the configuration set at the operation level will be used and the resource level ignored.
837+
838+
In the following example we use different attributes for the `GET` and `POST` operations:
839+
840+
<code-selector>
841+
842+
```php
843+
<?php
844+
// api/src/ApiResource/Book.php with Symfony or app/ApiResource/Book.php with Laravel
845+
namespace App\ApiResource;
846+
847+
use ApiPlatform\Metadata\ApiResource;
848+
use ApiPlatform\Metadata\Get;
849+
use ApiPlatform\Metadata\Post;
850+
851+
#[ApiResource]
852+
#[Get(
853+
normalizationContext: [
854+
'attributes' => ['id', 'title'],
855+
],
856+
)]
857+
#[Post(
858+
denormalizationContext: [
859+
'attributes' => ['title'],
860+
],
861+
normalizationContext: [
862+
'attributes' => ['id', 'title'],
863+
],
864+
)]
865+
class Book
866+
{
867+
public ?int $id = null;
868+
869+
public string $title = '';
870+
871+
public ?string $name = null;
872+
873+
public string $isbn = '';
874+
875+
// ...
876+
}
877+
```
878+
879+
```yaml
880+
# The YAML syntax is only supported for Symfony
881+
# api/config/api_platform/resources/Book.yaml
882+
App\ApiResource\Book:
883+
operations:
884+
ApiPlatform\Metadata\Get:
885+
normalizationContext:
886+
attributes: ["id", "title"]
887+
ApiPlatform\Metadata\Post:
888+
denormalizationContext:
889+
attributes: ["title"]
890+
normalizationContext:
891+
attributes: ["id", "title"]
892+
```
893+
894+
```xml
895+
<!-- The XML syntax is only supported for Symfony -->
896+
<!-- api/config/api_platform/resources.xml -->
897+
<?xml version="1.0" encoding="UTF-8" ?>
898+
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
899+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
900+
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
901+
https://api-platform.com/schema/metadata/resources-3.0.xsd">
902+
<resource class="App\ApiResource\Book">
903+
<operations>
904+
<operation class="ApiPlatform\Metadata\Get">
905+
<normalizationContext>
906+
<values>
907+
<value name="attributes">
908+
<values>
909+
<value>id</value>
910+
<value>title</value>
911+
</values>
912+
</value>
913+
</values>
914+
</normalizationContext>
915+
</operation>
916+
<operation class="ApiPlatform\Metadata\Post">
917+
<denormalizationContext>
918+
<values>
919+
<value name="attributes">
920+
<values>
921+
<value>title</value>
922+
</values>
923+
</value>
924+
</values>
925+
</denormalizationContext>
926+
<normalizationContext>
927+
<values>
928+
<value name="attributes">
929+
<values>
930+
<value>id</value>
931+
<value>title</value>
932+
</values>
933+
</value>
934+
</values>
935+
</normalizationContext>
936+
</operation>
937+
</operations>
938+
</resource>
939+
</resources>
940+
```
941+
942+
</code-selector>
943+
944+
The `id` and `title` attributes will be included in the document generated during both `GET` and
945+
`POST` operations because the operation-specific configuration is used. When writing (`POST`), only
946+
`title` is accepted in the request.
947+
948+
Refer to the [operations](operations.md) documentation to learn more.
949+
950+
## Embedding Relations With Serialization Attributes
951+
952+
When you include related resources (like an author in a book), you can control which attributes of
953+
those related resources are exposed by using nested attribute definitions. This is useful when you
954+
want to limit the data returned for related objects without creating separate operations.
955+
956+
In the following example, we use nested attribute filtering to expose only specific fields of
957+
related resources:
958+
959+
<code-selector>
960+
961+
```php
962+
<?php
963+
// api/src/ApiResource/Book.php with Symfony or app/ApiResource/Book.php with Laravel
964+
namespace App\ApiResource;
965+
966+
use ApiPlatform\Metadata\ApiResource;
967+
968+
#[ApiResource(
969+
normalizationContext: [
970+
'attributes' => ['id', 'title', 'author' => ['id', 'name']],
971+
],
972+
)]
973+
class Book
974+
{
975+
public ?int $id = null;
976+
977+
public string $title = '';
978+
979+
public Author $author;
980+
981+
public string $isbn = '';
982+
983+
// ...
984+
}
985+
```
986+
987+
```yaml
988+
# The YAML syntax is only supported for Symfony
989+
# api/config/api_platform/resources/Book.yaml
990+
App\ApiResource\Book:
991+
normalizationContext:
992+
attributes:
993+
- id
994+
- title
995+
- author:
996+
- id
997+
- name
998+
```
999+
1000+
```xml
1001+
<!-- The XML syntax is only supported for Symfony -->
1002+
<!-- api/config/api_platform/resources.xml -->
1003+
<?xml version="1.0" encoding="UTF-8" ?>
1004+
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
1005+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1006+
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
1007+
https://api-platform.com/schema/metadata/resources-3.0.xsd">
1008+
<resource class="App\ApiResource\Book">
1009+
<normalizationContext>
1010+
<values>
1011+
<value name="attributes">
1012+
<values>
1013+
<value>id</value>
1014+
<value>title</value>
1015+
<value key="author">
1016+
<values>
1017+
<value>id</value>
1018+
<value>name</value>
1019+
</values>
1020+
</value>
1021+
</values>
1022+
</value>
1023+
</values>
1024+
</normalizationContext>
1025+
</resource>
1026+
</resources>
1027+
```
1028+
1029+
</code-selector>
1030+
1031+
<code-selector>
1032+
1033+
```php
1034+
<?php
1035+
// api/src/ApiResource/Author.php with Symfony or app/ApiResource/Author.php with Laravel
1036+
namespace App\ApiResource;
1037+
1038+
use ApiPlatform\Metadata\ApiResource;
1039+
1040+
#[ApiResource]
1041+
class Author
1042+
{
1043+
public ?int $id = null;
1044+
1045+
public string $name;
1046+
1047+
// ...
1048+
}
1049+
```
1050+
1051+
```yaml
1052+
# The YAML syntax is only supported for Symfony
1053+
# api/config/api_platform/resources/Author.yaml
1054+
App\ApiResource\Author: ~
1055+
```
1056+
1057+
```xml
1058+
<!-- The XML syntax is only supported for Symfony -->
1059+
<!-- api/config/api_platform/resources.xml -->
1060+
<?xml version="1.0" encoding="UTF-8" ?>
1061+
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
1062+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
1063+
xsi:schemaLocation="https://api-platform.com/schema/metadata/resources-3.0
1064+
https://api-platform.com/schema/metadata/resources-3.0.xsd">
1065+
<resource class="App\ApiResource\Author" />
1066+
</resources>
1067+
```
1068+
1069+
</code-selector>
1070+
1071+
In this example, the `author` property will be included in the response with only its `id` and
1072+
`name` attributes visible. Other attributes of the Author resource will be filtered out. The nested
1073+
attribute syntax `'author' => ['id', 'name']` instructs the serializer to only expose those
1074+
specified attributes of the related object.
1075+
7301076
## Property Normalization Context for Symfony
7311077

7321078
If you want to change the (de)normalization context of a property, for instance if you want to

0 commit comments

Comments
 (0)