Skip to content

Commit 10fcd7b

Browse files
authored
Merge pull request #457 from soyuka/identifier-normalizer
Document identifier normalizers
2 parents a5a1082 + 4571ffb commit 10fcd7b

File tree

3 files changed

+128
-2
lines changed

3 files changed

+128
-2
lines changed

core/data-providers.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ final class BlogPostItemDataProvider implements ItemDataProviderInterface, Restr
100100
return BlogPost::class === $resourceClass;
101101
}
102102
103-
public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?BlogPost
103+
public function getItem(string $resourceClass, $identifiers, string $operationName = null, array $context = []): ?BlogPost
104104
{
105105
// Retrieve the blog post item from somewhere then return it or null if not found
106-
return new BlogPost($id);
106+
return new BlogPost($identifiers['id']);
107107
}
108108
}
109109
```

core/identifiers.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Identifiers
2+
3+
Every item operation has an identifier in it's URL. Although this identifier is usually a number, it can also be an `UUID`, a date, or the type of your choice.
4+
To help with your development experience, we introduced an identifier normalization process.
5+
6+
## Custom identifier normalizer
7+
8+
> In the following chapter, we're assuming that `App\Uuid` is a project-owned class that manages a time-based UUID.
9+
10+
Let's say you have the following class, which is identified by a `UUID` type. In this example, `UUID` is not a simple string but it's an object with many attributes.
11+
12+
```php
13+
<?php
14+
namespace App\Entity;
15+
16+
use App\Uuid;
17+
18+
/**
19+
* @ApiResource
20+
*/
21+
final class Person {
22+
/**
23+
* @type Uuid
24+
* @ApiProperty(identifier=true)
25+
*/
26+
public $code;
27+
}
28+
```
29+
30+
Once registered as an `ApiResource`, having an existing person, it will be accessible through the following URL: `/people/110e8400-e29b-11d4-a716-446655440000`.
31+
Note that the property identifying our resource is named `code`.
32+
33+
Let's create a `DataProvider` for the `Person` entity:
34+
35+
```php
36+
<?php
37+
namespace App\DataProvider;
38+
39+
use App\Entity\Person;
40+
use App\Uuid;
41+
42+
final class PersonDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface {
43+
44+
/**
45+
* {@inheritdoc}
46+
*/
47+
public function getItem(string $resourceClass, $identifiers, string $operationName = null, array $context = [])
48+
{
49+
// Our identifier is:
50+
// $id['code']
51+
// although it's a string, it's not an instance of Uuid and we wanted to retrieve the timestamp of our time-based uuid:
52+
// $id['code']->getTimestamp()
53+
}
54+
55+
/**
56+
* {@inheritdoc}
57+
*/
58+
public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
59+
{
60+
return $resourceClass === Person::class;
61+
}
62+
}
63+
```
64+
65+
To cover this use case, we need to `denormalize` the identifier to an instance of our `App\Uuid` class. This case is covered by an identifier denormalizer:
66+
67+
```php
68+
<?php
69+
namespace App\Identifier;
70+
71+
use ApiPlatform\Core\Exception\InvalidIdentifierException;
72+
use App\Uuid;
73+
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
74+
75+
final class UuidNormalizer implements DenormalizerInterface
76+
{
77+
/**
78+
* {@inheritdoc}
79+
*/
80+
public function denormalize($data, $class, $format = null, array $context = [])
81+
{
82+
try {
83+
return Uuid::fromString($data);
84+
} catch (InvalidUuidStringException $e) {
85+
throw new InvalidIdentifierException($e->getMessage());
86+
}
87+
}
88+
89+
/**
90+
* {@inheritdoc}
91+
*/
92+
public function supportsDenormalization($data, $type, $format = null)
93+
{
94+
return is_a($type, Uuid::class, true);
95+
}
96+
}
97+
```
98+
99+
Tag this service as a `api_platform.identifier.normalizer`:
100+
101+
```xml
102+
<service id="App\identifier\UuidNormalizer" class="App\identifier\UuidNormalizer" public="false">
103+
<tag name="api_platform.identifier.normalizer" />
104+
</service>
105+
```
106+
107+
```yaml
108+
services:
109+
App\identifier\UuidNormalizer:
110+
tags:
111+
- { name: api_platform.identifier.normalizer }
112+
```
113+
114+
Your `PersonDataProvider` will now work as expected!
115+
116+
117+
## Supported identifiers
118+
119+
ApiPlatform supports the following identifier types:
120+
121+
- `scalar` (string, integer)
122+
- `\DateTime` (uses the symfony `DateTimeNormalizer` internally, see [DateTimeIdentifierNormalizer](https://github.com/api-platform/core/blob/master/src/Identifier/Normalizer/DateTimeIdentifierNormalizer.php))
123+
- `\Ramsey\Uuid\Uuid` (see [UuidNormalizer](https://github.com/api-platform/core/blob/master/src/Bridge/RamseyUuid/Identifier/Normalizer/UuidNormalizer.php))

index.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@
119119
2. [Available Serializers](core/serialization.md#available-serializers)
120120
3. [Decorating a Serializer and Add Extra Data](core/serialization.md#decorating-a-serializer-and-add-extra-data)
121121
27. [Handling Data Transfer Objects (DTOs)](core/dto.md)
122+
28. [Identifiers](core/identifiers.md)
123+
1. [Custom identifier normalizer](core/identifiers.md#custom-identifier-normalizer)
124+
2. [Supported identifiers](core/identifiers.md#supported-identifiers)
122125

123126
## The Schema Generator Component: Generate Data Models from Open Vocabularies
124127

0 commit comments

Comments
 (0)