Skip to content

Commit c10bc75

Browse files
Fix MultipartDecoder to support more denormalization types
1 parent 129ea77 commit c10bc75

File tree

1 file changed

+63
-4
lines changed

1 file changed

+63
-4
lines changed

core/file-upload.md

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,10 +394,15 @@ We need to create our own decoder to do it:
394394
<?php
395395
// api/src/Encoder/MultipartDecoder.php
396396

397+
declare(strict_types=1);
398+
397399
namespace App\Encoder;
398400

401+
use Doctrine\Common\Collections\Collection;
399402
use Symfony\Component\HttpFoundation\RequestStack;
400403
use Symfony\Component\Serializer\Encoder\DecoderInterface;
404+
use ReflectionClass;
405+
use ReflectionNamedType;
401406

402407
final class MultipartDecoder implements DecoderInterface
403408
{
@@ -411,23 +416,77 @@ final class MultipartDecoder implements DecoderInterface
411416
{
412417
$request = $this->requestStack->getCurrentRequest();
413418

414-
if (!$request) {
419+
if (!$request || !isset($context['resource_class'])) {
415420
return null;
416421
}
417422

418-
return array_map(static function (string $element) {
423+
// Retrieve the target class from the context
424+
$targetClass = $context['resource_class'];
425+
426+
// Get the expected types for the target entity
427+
$expectedTypes = $this->getPropertyTypes($targetClass);
428+
429+
$arrayMapWithKeys = function (callable $callback, array $array) {
430+
$result = [];
431+
432+
foreach ($array as $key => $value) {
433+
$result[$key] = $callback($value, $key);
434+
}
435+
436+
return $result;
437+
};
438+
439+
$map = $arrayMapWithKeys(static function (string $element, string $key) use ($expectedTypes) {
419440
// Multipart form values will be encoded in JSON.
420441
$decoded = json_decode($element, true);
421442

422-
return \is_array($decoded) ? $decoded : $element;
423-
}, $request->request->all()) + $request->files->all();
443+
if (is_array($decoded)) {
444+
return $decoded;
445+
}
446+
447+
if ('null' == $element) {
448+
return null;
449+
}
450+
451+
if (!isset($expectedTypes[$key])) {
452+
return $element;
453+
}
454+
455+
return match ($expectedTypes[$key]) {
456+
'bool' => filter_var($element, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE),
457+
'int' => is_numeric($element) ? (int) $element : $element,
458+
'float' => is_numeric($element) ? (float) $element : $element,
459+
Collection::class => !is_array($element) ? [] : $element,
460+
default => $element,
461+
};
462+
}, $request->request->all());
463+
464+
// dd($map + $request->files->all());
465+
466+
return $map + $request->files->all();
424467
}
425468

426469
public function supportsDecoding(string $format): bool
427470
{
428471
return self::FORMAT === $format;
429472
}
473+
474+
private function getPropertyTypes(string $className): array
475+
{
476+
$reflectionClass = new ReflectionClass($className);
477+
$types = [];
478+
479+
foreach ($reflectionClass->getProperties() as $property) {
480+
$type = $property->getType();
481+
if ($type instanceof ReflectionNamedType) {
482+
$types[$property->getName()] = $type->getName();
483+
}
484+
}
485+
486+
return $types;
487+
}
430488
}
489+
431490
```
432491

433492
If you're not using `autowiring` and `autoconfiguring`, don't forget to register the service and tag it as `serializer.encoder`.

0 commit comments

Comments
 (0)