You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
To retrieve data exposed by the API, API Platform uses classes called **state providers**. A state provider using [Doctrine
4
-
ORM](https://www.doctrine-project.org/projects/orm.html) to retrieve data from a database, a state provider using
3
+
To retrieve data exposed by the API, API Platform uses classes called **state providers**.
4
+
5
+
With the Symfony variant, a state provider using [Doctrine
6
+
ORM](https://www.doctrine-project.org/projects/orm.html) is ready to retrieve data from a database and a state provider using
5
7
[Doctrine MongoDB ODM](https://www.doctrine-project.org/projects/mongodb-odm.html) to retrieve data from a document
6
-
database, and a state provider using [Elasticsearch-PHP](https://www.elastic.co/guide/en/elasticsearch/client/php-api/current/index.html)
7
-
to retrieve data from an Elasticsearch cluster are included with the library. The first one is enabled by default. These
8
-
state providers natively support paged collections and filters. They can be used as-is and are perfectly suited to common uses.
8
+
database.
9
+
10
+
With the Laravel variant, a state provider using [Eloquent ORM](https://laravel.com/docs/eloquent) to retrieve data from a relational database and a state provider.
11
+
12
+
The ORM providers are enabled by default, based on your framework variant (Eloquent or Doctrine will be set up).
13
+
14
+
15
+
These state providers natively support paged collections and filters. They can be used as-is and are perfectly suited to common uses.
9
16
10
17
However, you sometimes want to retrieve data from other sources such as another persistence layer or a webservice.
11
18
Custom state providers can be used to do so. A project can include as many state providers as needed. The first able to
12
19
retrieve data for a given resource will be used.
13
20
14
21
To do so you need to implement the `ApiPlatform\State\ProviderInterface`.
15
22
16
-
In the following examples we will create custom state providers for an entity class called `App\Entity\BlogPost`.
17
-
Note, that if your entity is not Doctrine-related, you need to flag the identifier property by using
23
+
In the following examples we will create custom state providers for Symfony entities and Laravel models:
24
+
- For Symfony we will create an entity class called `App\Entity\BlogPost`.
25
+
- For Laravel, we will create a model class called `App\Models\BlogPost`.
26
+
27
+
Note, that if your entity is not Doctrine-related or Eloquent-related, you need to flag the identifier property by using
18
28
`#[ApiProperty(identifier: true)` for things to work properly (see also [Entity Identifier Case](serialization.md#entity-identifier-case)).
19
29
20
30
## Creating a Custom State Provider
21
31
32
+
### Custom State Provider with Symfony
33
+
22
34
If the [Symfony MakerBundle](https://symfony.com/doc/current/bundles/SymfonyMakerBundle) is installed in your project,
23
35
you can use the following command to generate a custom state provider easily:
24
36
@@ -114,7 +126,7 @@ final class BlogPostProvider implements ProviderInterface
114
126
}
115
127
```
116
128
117
-
We then need to configure this same provider on the BlogPost `GetCollection` operation, or for every operations via the `ApiResource` attribute:
129
+
We then need to configure this same provider on the BlogPost `GetCollection` operation, or for every operation via the `ApiResource` attribute:
118
130
119
131
```php
120
132
<?php
@@ -129,11 +141,124 @@ use App\State\BlogPostProvider;
129
141
class BlogPost {}
130
142
```
131
143
144
+
#### Custom State Provider with Laravel
145
+
146
+
Using [Laravel Artisan Console](https://laravel.com/docs/artisan), you can generate a custom state provider easily with the following command:
147
+
148
+
```console
149
+
php artisan make:state-provider
150
+
```
151
+
152
+
Let's start with a State Provider for the URI: `/blog_posts/{id}`.
153
+
154
+
First, your `BlogPostProvider` has to implement the
final class BlogPostProvider implements ProviderInterface
171
+
{
172
+
private const DATA = [
173
+
'ab' => new BlogPost('ab'),
174
+
'cd' => new BlogPost('cd'),
175
+
];
176
+
177
+
public function provide(Operation $operation, array $uriVariables = [], array $context = []): BlogPost|null
178
+
{
179
+
return self::DATA[$uriVariables['id']] ?? null;
180
+
}
181
+
}
182
+
```
183
+
184
+
For the example, we store the list of our blog posts in an associative array (the `BlogPostProvider::DATA` constant).
185
+
186
+
As this operation expects a `BlogPost`, the `provide` methods return the instance of the `BlogPost` corresponding to the ID passed in the URL. If the ID doesn't exist in the associative array, `provide()` returns `null`. API Platform will automatically generate a 404 response if the provider returns `null`.
187
+
188
+
The `$uriVariables` parameter contains an array with the values of the URI variables.
189
+
190
+
To use this provider we need to configure the provider on the operation:
191
+
192
+
```php
193
+
<?php
194
+
// api/src/Models/BlogPost.php
195
+
196
+
namespace App\Models;
197
+
198
+
use ApiPlatform\Metadata\Get;
199
+
use App\State\BlogPostProvider;
200
+
201
+
#[Get(provider: BlogPostProvider::class)]
202
+
class BlogPost {}
203
+
```
204
+
205
+
Now let's say that we also want to handle the `/blog_posts` URI which returns a collection. We can change the Provider into
206
+
supporting a wider range of operations. Then we can provide a collection of blog posts when the operation is a `CollectionOperationInterface`:
207
+
208
+
```php
209
+
<?php
210
+
// api/src/State/BlogPostProvider.php
211
+
212
+
namespace App\State;
213
+
214
+
use App\Models\BlogPost;
215
+
use ApiPlatform\Metadata\Operation;
216
+
use ApiPlatform\State\ProviderInterface;
217
+
use ApiPlatform\Metadata\CollectionOperationInterface;
final class BlogPostProvider implements ProviderInterface
223
+
{
224
+
private const DATA = [
225
+
'ab' => new BlogPost('ab'),
226
+
'cd' => new BlogPost('cd'),
227
+
];
228
+
229
+
public function provide(Operation $operation, array $uriVariables = [], array $context = []): iterable|BlogPost|null
230
+
{
231
+
if ($operation instanceof CollectionOperationInterface) {
232
+
return self::DATA;
233
+
}
234
+
235
+
return self::DATA[$uriVariables['id']] ?? null;
236
+
}
237
+
}
238
+
```
239
+
240
+
We then need to configure this same provider on the BlogPost `GetCollection` operation, or for every operation via the `ApiResource` attribute:
241
+
242
+
```php
243
+
<?php
244
+
// api/src/Models/BlogPost.php
245
+
246
+
namespace App\Models;
247
+
248
+
use ApiPlatform\Metadata\ApiResource;
249
+
use App\State\BlogPostProvider;
250
+
251
+
#[ApiResource(provider: BlogPostProvider::class)]
252
+
class BlogPost {}
253
+
```
254
+
132
255
## Hooking into the Built-In State Provider
133
256
134
257
If you want to execute custom business logic before or after retrieving data, this can be achieved by [decorating](https://symfony.com/doc/current/service_container/service_decoration.html) the built-in state providers or using [composition](https://en.wikipedia.org/wiki/Object_composition).
135
258
136
-
The next example uses a [DTO](https://api-platform.com/docs/core/dto/#using-data-transfer-objects-dtos) to change the presentation for data originally retrieved by the default state provider.
259
+
The next examples (one for Symfony and one for Laravel) uses a [DTO](https://api-platform.com/docs/core/dto/#using-data-transfer-objects-dtos) to change the presentation for data originally retrieved by the default state provider.
260
+
261
+
### Symfony State Provider mechanism
137
262
138
263
```php
139
264
<?php
@@ -142,7 +267,7 @@ The next example uses a [DTO](https://api-platform.com/docs/core/dto/#using-data
142
267
namespace App\State;
143
268
144
269
use App\Dto\AnotherRepresentation;
145
-
use App\Model\Book;
270
+
use App\Entity\Book;
146
271
use ApiPlatform\Metadata\Operation;
147
272
use ApiPlatform\State\ProviderInterface;
148
273
use Symfony\Component\DependencyInjection\Attribute\Autowire;
@@ -187,7 +312,86 @@ use App\State\BookRepresentationProvider;
187
312
class Book {}
188
313
```
189
314
190
-
## Registering Services Without Autowiring
315
+
#### Laravel State Provider mechanism
316
+
317
+
First, don't forget to tag the service with the `ProviderInterface`
0 commit comments