Skip to content

Commit c494b63

Browse files
authored
Merge pull request #1391 from alanpoulain/merge-2.6
2 parents 3b0828e + 1afb843 commit c494b63

32 files changed

+1618
-464
lines changed

client-generator/nextjs.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ Install required dependencies:
2828

2929
$ npx @api-platform/client-generator https://demo.api-platform.com . --generator next --resource book
3030
# Replace the URL by the entrypoint of your Hydra-enabled API
31+
# Omit the resource flag to generate files for all resource types exposed by the API.
3132

32-
> Note: Omit the resource flag to generate files for all resource types exposed by the API.
33+
> Note: On the [API Platform distribution](https://github.com/api-platform/api-platform), you can run
34+
> `generate-api-platform-client` instead of `npx @api-platform/client-generator`.
3335
3436
## Starting the Project
3537

client-generator/react.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Omit the resource flag to generate files for all resource types exposed by the A
6161
If you don't use the standalone installation, run the following command instead:
6262

6363
```console
64-
npx @api-platform/client-generator https://demo.api-platform.com src/ --resource book
64+
npx @api-platform/client-generator https://demo.api-platform.com src/ --resource book -g react
6565
```
6666

6767
Replace the URL with the entrypoint of your Hydra-enabled API.

client-generator/troubleshooting.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,7 @@ cause: null } }
5555
Check access to the specified URL, in this case `https://demo.api-platform.com/contexts/Entrypoint`, use curl to check
5656
access and the response `curl https://demo.api-platform.com/contexts/Entrypoint`. In the above case an "Access Denied"
5757
message in JSON format was being returned.
58+
59+
## Docker distribution on Windows and hot-reloading
60+
61+
Due to [a long-time known Docker for Windows issue](https://forums.docker.com/t/file-system-watch-does-not-work-with-mounted-volumes/12038), the files changes on the host are not notified on the `pwa` container. It causes the hot-reloading feature to not working properly for Windows users.

client-generator/vuetify.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Install with Docker
44

5-
If you use the API Platform distribution with docker, first you have to add the [Vue CLI](https://cli.vuejs.org/guide/) to the `yarn global add` command in `client/Dockerfile`:
5+
If you use the API Platform distribution with docker, first you have to add the [Vue CLI](https://cli.vuejs.org/guide/) to the `yarn global add` command in `pwa/Dockerfile`:
66

77
```dockerfile
88
RUN yarn global add @api-platform/client-generator @vue/cli @vue/cli-service-global

core/configuration.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -425,8 +425,7 @@ api_platform:
425425
stateless: ~
426426

427427
# The URL generation strategy to use for IRIs
428-
# Use values from UrlGeneratorInterface
429-
url_generation_strategy: ~
428+
url_generation_strategy: !php/const ApiPlatform\Core\Api\UrlGeneratorInterface::ABS_PATH
430429

431430
# ...
432431
```

core/content-negotiation.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The API system has built-in [content negotiation](https://en.wikipedia.org/wiki/Content_negotiation) capabilities.
44

5-
By default, only the [JSON-LD](https://json-ld.org) format is enabled. However API Platform Core supports many more formats and can be extended.
5+
By default, only the [JSON-LD](https://json-ld.org) and JSON formats are enabled. However API Platform Core supports many more formats and can be extended.
66

77
The framework natively supports JSON-LD (and Hydra), GraphQL, JSON:API, HAL, YAML, CSV, HTML (API docs), raw JSON and raw XML.
88
Using the raw JSON or raw XML formats is discouraged, prefer using JSON-LD instead, which provides more feature and is as easy to use.
@@ -52,6 +52,7 @@ api_platform:
5252
yaml: ['application/x-yaml']
5353
csv: ['text/csv']
5454
html: ['text/html']
55+
myformat: ['application/vnd.myformat']
5556
```
5657
5758
To enable GraphQL support, [read the dedicated chapter](graphql.md).

core/controllers.md

Lines changed: 48 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,11 @@ First, let's create your custom operation:
3535
namespace App\Controller;
3636

3737
use App\Entity\Book;
38+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
39+
use Symfony\Component\HttpKernel\Attribute\AsController;
3840

39-
class CreateBookPublication
41+
#[AsController]
42+
class CreateBookPublication extends AbstractController
4043
{
4144
private $bookPublishingHandler;
4245

@@ -98,10 +101,10 @@ use App\Controller\CreateBookPublication;
98101
'path' => '/books/{id}/publication',
99102
'controller' => CreateBookPublication::class,
100103
],
101-
])
104+
])]
102105
class Book
103106
{
104-
//...
107+
// ...
105108
}
106109
```
107110

@@ -157,24 +160,20 @@ use ApiPlatform\Core\Annotation\ApiResource;
157160
use App\Controller\CreateBookPublication;
158161
use Symfony\Component\Serializer\Annotation\Groups;
159162

160-
/**
161-
* @ApiResource(itemOperations={
162-
* "get",
163-
* "post_publication"={
164-
* "method"="POST",
165-
* "path"="/books/{id}/publication",
166-
* "controller"=CreateBookPublication::class,
167-
* "normalization_context"={"groups"={"publication"}},
168-
* }
169-
* })
170-
*/
163+
#[ApiResource(itemOperations: [
164+
'get',
165+
'post_publication' => [
166+
'method' => 'POST',
167+
'path' => '/books/{id}/publication',
168+
'controller' => CreateBookPublication::class,
169+
'normalization_context' => ['groups' => 'publication'],
170+
],
171+
])]
171172
class Book
172173
{
173-
//...
174+
// ...
174175

175-
/**
176-
* @Groups("publication")
177-
*/
176+
#[Groups(['publication'])]
178177
public $isbn;
179178

180179
// ...
@@ -236,20 +235,18 @@ operation attribute:
236235
use ApiPlatform\Core\Annotation\ApiResource;
237236
use App\Controller\CreateBookPublication;
238237

239-
/**
240-
* @ApiResource(itemOperations={
241-
* "get",
242-
* "post_publication"={
243-
* "method"="POST",
244-
* "path"="/books/{id}/publication",
245-
* "controller"=CreateBookPublication::class,
246-
* "read"=false,
247-
* }
248-
* })
249-
*/
238+
#[ApiResource(itemOperations: [
239+
'get',
240+
'post_publication' => [
241+
'method' => 'POST',
242+
'path' => '/books/{id}/publication',
243+
'controller' => CreateBookPublication::class,
244+
'read' => false,
245+
],
246+
])]
250247
class Book
251248
{
252-
//...
249+
// ...
253250
}
254251
```
255252

@@ -312,16 +309,14 @@ First, let's create your resource configuration:
312309

313310
use ApiPlatform\Core\Annotation\ApiResource;
314311

315-
/**
316-
* @ApiResource(itemOperations={
317-
* "get",
318-
* "post_publication"={"route_name"="book_post_publication"},
319-
* "book_post_discontinuation",
320-
* })
321-
*/
312+
#[ApiResource(itemOperations: [
313+
'get',
314+
'post_publication' => ['route_name' => 'book_post_publication'],
315+
'book_post_discontinuation',
316+
])]
322317
class Book
323318
{
324-
//...
319+
// ...
325320
}
326321
```
327322

@@ -367,9 +362,12 @@ and its related route using annotations:
367362
namespace App\Controller;
368363

369364
use App\Entity\Book;
365+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
366+
use Symfony\Component\HttpKernel\Attribute\AsController;
370367
use Symfony\Component\Routing\Annotation\Route;
371368

372-
class CreateBookPublication
369+
#[AsController]
370+
class CreateBookPublication extends AbstractController
373371
{
374372
private $bookPublishingHandler;
375373

@@ -378,17 +376,15 @@ class CreateBookPublication
378376
$this->bookPublishingHandler = $bookPublishingHandler;
379377
}
380378

381-
/**
382-
* @Route(
383-
* name="book_post_publication",
384-
* path="/books/{id}/publication",
385-
* methods={"POST"},
386-
* defaults={
387-
* "_api_resource_class"=Book::class,
388-
* "_api_item_operation_name"="post_publication"
389-
* }
390-
* )
391-
*/
379+
#[Route(
380+
name: 'book_post_publication',
381+
path: '/books/{id}/publication',
382+
methods: ['POST'],
383+
defaults: [
384+
'_api_resource_class' => Book::class,
385+
'_api_item_operation_name' => 'post_publication',
386+
],
387+
)]
392388
public function __invoke(Book $book): Book
393389
{
394390
$this->bookPublishingHandler->handle($book);
@@ -412,7 +408,9 @@ namespace App\Controller;
412408

413409
use App\Entity\Book;
414410
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
411+
use Symfony\Component\HttpKernel\Attribute\AsController;
415412

413+
#[AsController]
416414
class BookController extends AbstractController
417415
{
418416
public function createPublication(Book $book, BookPublishingHandler $bookPublishingHandler): Book

core/data-persisters.md

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ services:
155155

156156
## Calling multiple DataPersisters
157157

158-
Our DataPersisters are called in chain, once a data persister is supported the chain breaks and API Platform assumes your data is persisted. You can call mutliple data persisters by implementing the `ResumableDataPersisterInterface`:
158+
Our DataPersisters are called in chain, once a data persister is supported the chain breaks and API Platform assumes your data is persisted. You can call multiple data persisters by implementing the `ResumableDataPersisterInterface`:
159159

160160
```php
161161
namespace App\DataPersister;
@@ -189,4 +189,53 @@ final class BlogPostDataPersister implements ContextAwareDataPersisterInterface,
189189
}
190190
```
191191

192-
This is very useful when using [`Messenger` with API Platform](messenger.md) as you may want to do something asynchronously with the data but still call the default Doctrine data persister.
192+
This is very useful when using [`Messenger` with API Platform](messenger.md) as you may want to do something asynchronously with the data but still call the default Doctrine data persister, for example:
193+
```php
194+
namespace App\DataPersister;
195+
196+
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
197+
use Doctrine\ORM\EntityManagerInterface;
198+
use App\Entity\BlogPost;
199+
200+
final class BlogPostDataPersister implements ContextAwareDataPersisterInterface, ResumableDataPersisterInterface
201+
{
202+
private $entityManager;
203+
204+
public function __construct(EntityManagerInterface $entityManager)
205+
{
206+
$this->entityManager = $entityManager;
207+
}
208+
209+
public function supports($data, array $context = []): bool
210+
{
211+
return $data instanceof BlogPost;
212+
}
213+
214+
public function persist($data, array $context = [])
215+
{
216+
$this->entityManager->persist($data);
217+
$this->entityManager->flush();
218+
}
219+
220+
public function remove($data, array $context = [])
221+
{
222+
$this->entityManager->remove($data);
223+
$this->entityManager->flush();
224+
}
225+
226+
// Once called this data persister will resume to the next one
227+
public function resumable(array $context = []): bool
228+
{
229+
return true;
230+
}
231+
}
232+
```
233+
```yaml
234+
# api/config/services.yaml
235+
services:
236+
# ...
237+
App\DataPersister\BlogPostDataPersister: ~
238+
# Uncomment only if autoconfiguration is disabled
239+
#arguments: ['@App\DataPersister\BlogPostDataPersister.inner']
240+
#tags: [ 'api_platform.data_persister' ]
241+
```

core/default-order.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ API Platform Core provides an easy way to override the default order of items in
55
By default, items in the collection are ordered in ascending (ASC) order by their resource identifier(s). If you want to
66
customize this order, you must add an `order` attribute on your ApiResource annotation:
77

8+
[codeSelector]
9+
810
```php
911
<?php
1012
// api/src/Entity/Book.php
@@ -28,9 +30,21 @@ class Book
2830
}
2931
```
3032

33+
```yaml
34+
# api/config/api_platform/resources/Book.yaml
35+
App\Entity\Book:
36+
attributes:
37+
order:
38+
foo: ASC
39+
```
40+
41+
[/codeSelector]
42+
3143
This `order` attribute is used as an array: the key defines the order field, the values defines the direction.
3244
If you only specify the key, `ASC` direction will be used as default. For example, to order by `foo` & `bar`:
3345

46+
[codeSelector]
47+
3448
```php
3549
<?php
3650
// api/src/Entity/Book.php
@@ -59,8 +73,19 @@ class Book
5973
}
6074
```
6175

76+
```yaml
77+
# api/config/api_platform/resources/Book.yaml
78+
App\Entity\Book:
79+
attributes:
80+
order: ['foo', 'bar']
81+
```
82+
83+
[/codeSelector]
84+
6285
It's also possible to configure the default order on an association property:
6386

87+
[codeSelector]
88+
6489
```php
6590
<?php
6691
// api/src/Entity/Book.php
@@ -84,8 +109,19 @@ class Book
84109
}
85110
```
86111

112+
```yaml
113+
# api/config/api_platform/resources/Book.yaml
114+
App\Entity\Book:
115+
attributes:
116+
order: ['author.username']
117+
```
118+
119+
[/codeSelector]
120+
87121
Another possibility is to apply the default order for a specific collection operation, which will override the global default order configuration.
88122

123+
[codeSelector]
124+
89125
```php
90126
/**
91127
* collectionOperations={
@@ -106,3 +142,21 @@ class Book
106142
// ...
107143
}
108144
```
145+
146+
```yaml
147+
# api/config/api_platform/resources/Book.yaml
148+
App\Entity\Book:
149+
get: ~
150+
get_desc_custom:
151+
method: get
152+
path: custom_collection_desc_foos
153+
order:
154+
name: DESC
155+
get_asc_custom:
156+
method: get
157+
path: custom_collection_asc_foos
158+
order:
159+
name: ASC
160+
```
161+
162+
[/codeSelector]

0 commit comments

Comments
 (0)