Skip to content

Commit

Permalink
docs(serialization): support for laravel and fix image
Browse files Browse the repository at this point in the history
  • Loading branch information
vinceAmstoutz committed Nov 25, 2024
1 parent de4ec84 commit 643b62a
Showing 1 changed file with 81 additions and 13 deletions.
94 changes: 81 additions & 13 deletions core/serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,24 @@

## Overall Process

API Platform embraces and extends the Symfony Serializer Component to transform PHP entities in (hypermedia) API responses.
API Platform embraces and extends the Symfony Serializer Component & Eloquent Serialization to transform PHP entities in
(hypermedia) API responses.

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

The main serialization process has two stages:

![Serializer workflow](../core/images/SerializerWorkflow.png)
![Serializer workflow](images/SerializerWorkflow.png)

> As you can see in the picture above, an array is used as a man-in-the-middle. This way, Encoders will only deal with turning specific formats into arrays and vice versa. The same way, Normalizers will deal with turning specific objects into arrays and vice versa.
> -- [The Symfony documentation](https://symfony.com/doc/current/components/serializer.html)
Unlike Symfony itself, API Platform leverages custom normalizers, its router and the [state provider](state-providers.md) system to perform an advanced transformation. Metadata are added to the generated document including links, type information, pagination data or available filters.
Unlike Symfony or Laravel themselves, API Platform leverages custom normalizers, its router and the [state provider](state-providers.md)
system to perform an advanced transformation. Metadata are added to the generated document including links, type
information, pagination data or available filters.

The API Platform Serializer is extendable. You can register custom normalizers and encoders in order to support other formats. You can also decorate existing normalizers to customize their behaviors.
The API Platform Serializer is extendable. You can register custom normalizers and encoders in order to support other formats.
You can also decorate existing normalizers to customize their behaviors.

## Available Serializers

Expand All @@ -41,7 +45,7 @@ feature of the Symfony Serializer component.
In addition to groups, you can use any option supported by the Symfony Serializer. For example, you can use [`enable_max_depth`](https://symfony.com/doc/current/components/serializer.html#handling-serialization-depth)
to limit the serialization depth.

### Configuration
### Configuration for Symfony

Just like other Symfony and API Platform components, the Serializer component can be configured using attributes, XML
or YAML. Since attributes are easy to understand, we will use them in the following examples.
Expand Down Expand Up @@ -69,6 +73,9 @@ framework:

## Using Serialization Groups

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

It is simple to specify what groups to use in the API system:

1. Add the normalization context and denormalization context attributes to the resource, and specify which groups to use. Here you see that we add `read` and `write`, respectively. You can use any group names you wish.
Expand Down Expand Up @@ -101,6 +108,7 @@ class Book
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources.yaml
resources:
App\Entity\Book:
Expand All @@ -109,6 +117,7 @@ resources:
denormalizationContext:
groups: ['write']
# The YAML syntax is only supported for Symfony
# api/config/serialization/Book.yaml
App\Entity\Book:
attributes:
Expand All @@ -119,6 +128,7 @@ App\Entity\Book:
```

```xml
<!--The XML syntax is only supported for Symfony -->
<!-- api/config/api_platform/resources.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
Expand Down Expand Up @@ -146,7 +156,8 @@ App\Entity\Book:
</denormalizationContext>
</resource>
</resources>
<!--The XML syntax is only supported for Symfony -->
<!-- api/config/serialization/Book.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<serializer xmlns="http://symfony.com/schema/dic/serializer-mapping"
Expand Down Expand Up @@ -184,6 +195,15 @@ documentation generator.

## Using Serialization Groups per Operation

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

<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>

By default, the serializer provided with API Platform represents relations between objects using [dereferenceable IRIs](https://en.wikipedia.org/wiki/Internationalized_Resource_Identifier).
They allow you to retrieve details for related objects by issuing extra HTTP requests. However, for performance reasons, it is sometimes preferable to avoid forcing the client to issue extra HTTP requests.


It is possible to specify normalization and denormalization contexts (as well as any other attribute) on a per-operation
basis. API Platform will always use the most specific definition. For instance, if normalization groups are set both
at the resource level and at the operation level, the configuration set at the operation level will be used and the resource
Expand Down Expand Up @@ -219,6 +239,7 @@ class Book
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Book.yaml
App\Entity\Book:
normalizationContext:
Expand All @@ -229,6 +250,7 @@ App\Entity\Book:
normalizationContext:
groups: ['patch']
# The YAML syntax is only supported for Symfony
# api/config/serializer/Book.yaml
App\Entity\Book:
attributes:
Expand All @@ -239,6 +261,7 @@ App\Entity\Book:
```

```xml
<!-- The XML syntax is only supported for Symfony -->
<!-- api/config/api_platform/resources.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<resources xmlns="https://api-platform.com/schema/metadata/resources-3.0"
Expand Down Expand Up @@ -272,6 +295,7 @@ App\Entity\Book:
</resource>
</resources>
<!-- The XML syntax is only supported for Symfony -->
<!-- api/config/serialization/Book.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<serializer xmlns="http://symfony.com/schema/dic/serializer-mapping"
Expand Down Expand Up @@ -309,6 +333,9 @@ They allow you to retrieve details for related objects by issuing extra HTTP req

### Normalization

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

In the following JSON document, the relation from a book to an author is by default represented by an URI:

```json
Expand Down Expand Up @@ -350,11 +377,13 @@ class Book
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Book.yaml
App\Entity\Book:
normalizationContext:
groups: ['book']
# The YAML syntax is only supported for Symfony
# api/config/serializer/Book.yaml
App\Entity\Book:
attributes:
Expand Down Expand Up @@ -387,6 +416,7 @@ class Person
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/serializer/Person.yaml
App\Entity\Person:
attributes:
Expand Down Expand Up @@ -420,6 +450,9 @@ Instead of embedding relations in the main HTTP response, you may want [to "push

### Denormalization

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

It is also possible to embed a relation in `PUT`, `PATCH` and `POST` requests. To enable that feature, set the serialization groups
the same way as normalization. For example:

Expand All @@ -440,6 +473,7 @@ class Book
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Book.yaml
App\Entity\Book:
denormalizationContext:
Expand All @@ -458,6 +492,9 @@ You can specify as many embedded relation levels as you want.

### Force IRI with relations of the same type (parent/childs relations)

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

It is a common problem to have entities that reference other entities of the same type:

<code-selector>
Expand Down Expand Up @@ -490,13 +527,15 @@ class Person
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Person.yaml
App\Entity\Person:
normalizationContext:
groups: ['person']
denormalizationContext:
groups: ['person']
# The YAML syntax is only supported for Symfony
# api/config/serializer/Person.yaml
App\Entity\Person:
attributes:
Expand Down Expand Up @@ -542,6 +581,7 @@ class Person
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Person.yaml
resources:
App\Entity\Person:
Expand All @@ -555,6 +595,7 @@ properties:
readableLink: false
writableLink: false
# The YAML syntax is only supported for Symfony
# api/config/serializer/Person.yaml
App\Entity\Person:
attributes:
Expand All @@ -566,7 +607,7 @@ App\Entity\Person:

</code-selector>

### Plain Identifiers
### Plain Identifiers for Symfony

Instead of sending an IRI to set a relation, you may want to send a plain identifier. To do so, you must create your own denormalizer:

Expand Down Expand Up @@ -617,7 +658,7 @@ class PlainIdentifierDenormalizer implements DenormalizerInterface, Denormalizer
}
```

## Property Normalization Context
## Property Normalization Context for Symfony

If you want to change the (de)normalization context of a property, for instance if you want to change the format of the date time,
you can do so by using the `#[Context]` attribute from the Symfony Serializer component.
Expand Down Expand Up @@ -710,6 +751,9 @@ class Book

## Calculated Field

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

Sometimes you need to expose calculated fields. This can be done by leveraging the groups. This time not on a property, but on a method.

<code-selector>
Expand Down Expand Up @@ -755,13 +799,15 @@ class Greeting
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Greeting.yaml
App\Entity\Greeting:
operations:
ApiPlatform\Metadata\GetCollection:
normalizationContext:
groups: 'greeting:collection:get'
# The YAML syntax is only supported for Symfony
# api/config/serializer/Greeting.yaml
App\Entity\Greeting:
attributes:
Expand All @@ -777,6 +823,9 @@ App\Entity\Greeting:

## Changing the Serialization Context Dynamically

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

<p align="center" class="symfonycasts"><a href="https://symfonycasts.com/screencast/api-platform-security/service-decoration?cid=apip"><img src="../symfony/images/symfonycasts-player.png" alt="Context Builder & Service Decoration screencast"><br>Watch the Context Builder & Service Decoration screencast</a></p>

Let's imagine a resource where most fields can be managed by any user, but some can be managed only by admin users:
Expand Down Expand Up @@ -816,13 +865,15 @@ class Book
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Book.yaml
App\Entity\Book:
normalizationContext:
groups: ['book:output']
denormalizationContext:
groups: ['book:input']
# The YAML syntax is only supported for Symfony
# api/config/serializer/Book.yaml
App\Entity\Book:
attributes:
Expand All @@ -842,6 +893,7 @@ API Platform implements a `ContextBuilder`, which prepares the context for seria
`createFromRequest` method:

```yaml
# The YAML syntax is only supported for Symfony
# api/config/services.yaml
services:
# ...
Expand Down Expand Up @@ -890,7 +942,7 @@ If the user has the `ROLE_ADMIN` permission and the subject is an instance of Bo
denormalization context. The `$normalization` variable lets you check whether the context is for normalization (if `TRUE`) or denormalization
(`FALSE`).

## Changing the Serialization Context on a Per-item Basis
## Changing the Serialization Context on a Per-item Basis for Symfony

The example above demonstrates how you can modify the normalization/denormalization context based on the current user
permissions for all books. Sometimes, however, the permissions vary depending on what book is being processed.
Expand Down Expand Up @@ -973,7 +1025,7 @@ instance.
Note: In this example, we use the `TokenStorageInterface` to verify access to the book instance. However, Symfony
provides many useful other services that might be better suited to your use case. For example, the [`AuthorizationChecker`](https://symfony.com/doc/current/components/security/authorization.html#authorization-checker).

## Name Conversion
## Name Conversion for Symfony

The Serializer Component provides a handy way to map PHP field names to serialized names. See the related [Symfony documentation](https://symfony.com/doc/current/components/serializer.html#converting-property-names-when-serializing-and-deserializing).

Expand All @@ -994,7 +1046,7 @@ api_platform:

If symfony's `MetadataAwareNameConverter` is available it'll be used by default. If you specify one in ApiPlatform configuration, it'll be used. Note that you can use decoration to benefit from this name converter in your own implementation.

## Decorating a Serializer and Adding Extra Data
## Decorating a Serializer and Adding Extra Data for Symfony

In the following example, we will see how we add extra information to the serialized output. Here is how we add the
date on each request in `GET`:
Expand Down Expand Up @@ -1076,8 +1128,15 @@ final class ApiNormalizer implements NormalizerInterface, DenormalizerInterface,

## Entity Identifier Case

API Platform is able to guess the entity identifier using Doctrine metadata ([ORM](https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/basic-mapping.html#identifiers-primary-keys), [MongoDB ODM](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/current/reference/basic-mapping.html#identifiers)).
For ORM, it also supports [composite identifiers](https://www.doctrine-project.org/projects/doctrine-orm/en/current/tutorials/composite-primary-keys.html).
> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

API Platform is able to guess the entity identifier using Doctrine metadata ([ORM](https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/basic-mapping.html#identifiers-primary-keys),
[MongoDB ODM](https://www.doctrine-project.org/projects/doctrine-mongodb-odm/en/current/reference/basic-mapping.html#identifiers))
or Laravel Eloquent metadata ([ORM](https://laravel.com/docs/eloquent#primary-keys)).

For ORM, it also supports [Doctrine composite identifiers](https://www.doctrine-project.org/projects/doctrine-orm/en/current/tutorials/composite-primary-keys.html)
and [Eloquent composite identifiers](https://laravel.com/docs/eloquent#composite-primary-keys).

If you are not using the Doctrine ORM or MongoDB ODM Provider, you must explicitly mark the identifier using the `identifier` attribute of
the `ApiPlatform\Metadata\ApiProperty` annotation. For example:
Expand Down Expand Up @@ -1115,6 +1174,7 @@ class Book
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/properties.yaml
properties:
App\Entity\Book:
Expand All @@ -1124,6 +1184,7 @@ properties:

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!-- The XML syntax is only supported for Symfony -->
<!-- api/config/api_platform/properties.xml -->
<properties xmlns="https://api-platform.com/schema/metadata/properties-3.0"
Expand All @@ -1147,6 +1208,9 @@ must do the following:

## Embedding the JSON-LD Context

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

By default, the generated [JSON-LD context](https://www.w3.org/TR/json-ld/#the-context) (`@context`) is only referenced by
an IRI. A client that uses JSON-LD must send a second HTTP request to retrieve it:

Expand Down Expand Up @@ -1180,6 +1244,7 @@ class Book
```

```yaml
# The YAML syntax is only supported for Symfony
# api/config/api_platform/resources/Book.yaml
App\Entity\Book:
normalizationContext:
Expand Down Expand Up @@ -1207,6 +1272,9 @@ The JSON output will now include the embedded context:

## Collection Relation

> [!NOTE]
> In Symfony, we use the term “Entity”, which is equivalent to Laravel “Models”.

This is a special case where, in an entity, you have a `toMany` relation. By default, Doctrine will use an `ArrayCollection` to store your values. This is fine when you have a _read_ operation, but when you try to _write_ you can observe an issue where the response is not reflecting the changes correctly. It can lead to client errors even though the update was correct.
Indeed, after an update on this relation, the collection looks wrong because `ArrayCollection`'s indices are not sequential. To change this, we recommend to use a getter that returns `$collectionRelation->getValues()`. Thanks to this, the relation is now a real array which is sequentially indexed.

Expand Down

0 comments on commit 643b62a

Please sign in to comment.