Skip to content

Commit 67485f7

Browse files
committed
fix #1856
1 parent 9e67aec commit 67485f7

2 files changed

Lines changed: 51 additions & 102 deletions

File tree

core/bootstrap.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
You may want to run a minimal version of API Platform. This one file runs API Platform (without GraphQL, Eloquent, Doctrine MongoDB...).
44
It requires the following Composer packages:
55

6+
> [!NOTE]
7+
> This documentation is outdated we're working on improving it, in the mean time we declare
8+
most of the services manually in the [ApiPlatformProvider](https://github.com/api-platform/core/blob/64768a6a5b480e1b8e33c639fb28b27883c69b79/src/Laravel/ApiPlatformProvider.php) it can be source of inspiration.
9+
610
```console
711
composer require \
812
api-platform/core \

core/form-data.md

Lines changed: 47 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -16,128 +16,73 @@ of the framework.
1616
> If you're working with Laravel, refer to the [Laravel CSRF documentation](https://laravel.com/docs/csrf) to ensure
1717
> adequate protection against such attacks.
1818
19-
In this tutorial, we will decorate the default `DeserializeListener` class to handle form data if applicable, and delegate to the built-in listener for other cases.
19+
## Configuration
2020

21-
## Create your `FormRequestProcessorDecorator` processor
21+
First, you must register the form format and map it to the `application/x-www-form-urlencoded` MIME type in your API Platform configuration:
2222

23-
This decorator is able to denormalize posted form data to the target object. In case of other format, it fallbacks to the original [DeserializeListener](https://github.com/api-platform/core/blob/91dc2a4d6eeb79ea8dec26b41e800827336beb1a/src/Bridge/Symfony/Bundle/Resources/config/api.xml#L85-L91).
24-
25-
```php
26-
<?php
27-
// api/src/State/FormRequestProcessorDecorator.php using Symfony or app/State/FormRequestProcessorDecorator.php using Laravel
28-
29-
namespace App\State;
30-
31-
use ApiPlatform\State\ProcessorInterface;
32-
use Symfony\Component\HttpFoundation\Request;
33-
use ApiPlatform\Metadata\Operation;
34-
use ApiPlatform\State\SerializerContextBuilderInterface;
35-
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
36-
37-
final class FormRequestProcessorDecorator implements ProcessorInterface
38-
{
39-
public function __construct(
40-
private readonly ProcessorInterface $decorated,
41-
private readonly DenormalizerInterface $denormalizer,
42-
private readonly SerializerContextBuilderInterface $serializerContextBuilder
43-
) {}
44-
45-
public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): mixed
46-
{
47-
// If the content type is form data, we process it separately
48-
if ('form' === $data->getContentType()) {
49-
return $this->handleFormRequest($data);
50-
}
51-
52-
// Delegate the processing to the original processor for other cases
53-
return $this->decorated->process($data, $operation, $uriVariables, $context);
54-
}
23+
```yaml
24+
# api\_platform.yaml
25+
api_platform:
26+
formats:
27+
jsonld: ['application/ld+json']
28+
form: ['application/x-www-form-urlencoded']
29+
```
5530
56-
/**
57-
* Handle form requests by deserializing the data into the correct entity
58-
*/
59-
private function handleFormRequest(Request $request)
60-
{
61-
$attributes = $request->attributes->get('_api_attributes');
62-
if (!$attributes) {
63-
return null;
64-
}
31+
## Creating a Decoder
6532
66-
$context = $this->serializerContextBuilder->createFromRequest($request, false, $attributes);
33+
The Symfony Serializer (used by API Platform) does not decode `application/x-www-form-urlencoded` by default.
34+
You need to create a custom decoder that implements DecoderInterface to handle this format.
6735

68-
// Deserialize the form data into an entity
69-
$data = $request->request->all();
70-
71-
return $this->denormalizer->denormalize($data, 'App\Entity\SomeEntity', null, $context);
72-
}
73-
}
74-
```
36+
```php
37+
<?php
38+
// src/Serializer/FormUrlEncodedDecoder.php
7539
76-
Next, configure the `FormRequestProcessorDecorator` according to whether you're using Symfony or Laravel, as shown below:
40+
namespace App\Serializer;
7741
78-
### Creating the Service Definition using Symfony
42+
use Symfony\Component\Serializer\Encoder\DecoderInterface;
7943
80-
```yaml
81-
# api/config/services.yaml
82-
services:
83-
# ...
84-
App\State\FormRequestProcessorDecorator:
85-
decorates: api_platform.state.processor
86-
arguments:
87-
$decorated: '@App\State\FormRequestProcessorDecorator.inner'
88-
$denormalizer: '@serializer'
89-
$serializerContextBuilder: '@api_platform.serializer.context_builder'
90-
tags:
91-
- { name: 'api_platform.state.processor' }
92-
```
44+
class FormUrlEncodedDecoder implements DecoderInterface
45+
{
46+
public function decode(string $data, string $format, array $context = []): mixed
47+
{
48+
if (\!is_string($data)) {
49+
throw new \InvalidArgumentException('Data must be a string.');
50+
}
9351
94-
### Registering a Decorated Processor using Laravel
52+
parse_str($data, $parsedData);
9553
96-
```php
97-
<?php
98-
// app/Providers/AppServiceProvider.php
99-
100-
namespace App\Providers;
101-
102-
use Illuminate\Support\ServiceProvider;
103-
use App\State\FormRequestProcessorDecorator;
104-
use ApiPlatform\State\ProcessorInterface;
105-
use ApiPlatform\State\SerializerContextBuilderInterface;
106-
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
107-
108-
class AppServiceProvider extends ServiceProvider
109-
{
110-
public function register()
111-
{
112-
$this->app->bind(ProcessorInterface::class, function ($app) {
113-
$decoratedProcessor = $app->make(ProcessorInterface::class);
114-
115-
return new FormRequestProcessorDecorator(
116-
$decoratedProcessor,
117-
$app->make(DenormalizerInterface::class),
118-
$app->make(SerializerContextBuilderInterface::class)
119-
);
120-
});
54+
return $parsedData;
12155
}
56+
57+
public function supportsDecoding(string $format): bool
58+
{
59+
return $format === 'form';
60+
}
12261
}
12362
```
12463

125-
## Using your `FormRequestProcessorDecorator` processor
64+
## **Usage in a Resource**
12665

127-
Finally, you can use the processor in your API Resource like this:
66+
You can now configure your API Resource to accept the form input format. In this example, we define a FormData resource and restrict the inputFormats for the Post operation.
12867

12968
```php
130-
<?php
131-
// api/src/ApiResource/SomeEntity.php with Symfony or app/ApiResource/SomeEntity.php with Laravel
69+
<?php
70+
// src/ApiResource/FormData.php
13271
13372
namespace App\ApiResource;
13473
74+
use ApiPlatform\Metadata\Operation;
13575
use ApiPlatform\Metadata\Post;
136-
use App\State\FormRequestProcessorDecorator;
13776
138-
#[Post(processor: FormRequestProcessorDecorator::class)]
139-
class SomeEntity
140-
{
141-
//...
142-
}
77+
#[Post(
78+
inputFormats: ['form' => ['application/x-www-form-urlencoded']],
79+
processor: [self::class, 'process']
80+
)]
81+
class FormData {
82+
public string $name;
83+
84+
public static function process(mixed $data, Operation $operation, array $uriVariables, array $context) {
85+
return $data;
86+
}
87+
}
14388
```

0 commit comments

Comments
 (0)