Skip to content

Add UploadedFileStream PSR-7 decorator for multipart file uploads#21

Open
aviator-ua wants to merge 4 commits into
auto1-oss:masterfrom
aviator-ua:support-multiform-data
Open

Add UploadedFileStream PSR-7 decorator for multipart file uploads#21
aviator-ua wants to merge 4 commits into
auto1-oss:masterfrom
aviator-ua:support-multiform-data

Conversation

@aviator-ua

@aviator-ua aviator-ua commented May 23, 2026

Copy link
Copy Markdown

Summary

Adds end-to-end multipart/form-data support to the request serializer:

  • Multipart\UploadedFileStream — a PSR-7 StreamInterface decorator that carries a
    Symfony UploadedFile's filename and MIME type (exposed via getMetadata('filename')
    / getMetadata('mime-type')) alongside the underlying stream, plus a getUploadedFile()
    escape hatch for code needing Symfony-specific file operations.
  • Service\Serializer\Normalizer\StreamInterfaceDenormalizer — passes StreamInterface
    values through denormalization untouched, so binary DTO properties survive the request
    serializer instead of being mangled.

Context

Part of the rollout to support multipart/form-data across the auto1-oss API stack.

Changes

  • Multipart/UploadedFileStream.php — new PSR-7 decorator (see Summary).
  • Service/Serializer/Normalizer/StreamInterfaceDenormalizer.php — new pass-through denormalizer.
  • Resources/config/symfony_services.yml — registers the new denormalizer in
    auto1.api.request.serializer; migrates serializer service definitions to named arguments;
    sets parent: serializer.encoder.json on the JSON encoder; wires
    serializer.name_converter.metadata_aware into the object normalizer; passes the datetime
    format via defaultContext.
  • composer.json — adds psr/http-message and symfony/http-foundation dependencies.
  • Service/Logger/LoggerAwareTrait.php — adds license header; setLogger() now returns void.
  • TestsTests/Multipart/UploadedFileStreamTest.php,
    Tests/Serializer/Normalizer/StreamInterfaceDenormalizerTest.php.

Architectural notes

Location

Placed at top-level Multipart/ rather than Service/Multipart/. The class is a stateful
per-request wrapper, not a DI-registered service — grouping by domain (like Symfony's
HttpFoundation\File\UploadedFile) reads more accurately than appending to Service/.

No factory

Construction is a single line at one call site today (MultipartRequestDataExtractor::wrapFiles,
in the handler bundle). A factory would just relocate the PSR-17 StreamFactoryInterface
dependency one layer deeper without removing duplication. Worth revisiting if a second call
site appears or if construction grows real logic.

Open question

The handler-bundle's extractor recursively walks nested file arrays (<input name="foo[bar]">
shape). Generated DTOs currently declare each binary property as a flat top-level
StreamInterface, so the nested walk is defensive for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant