feat(object-mapper): Honor the output class in ObjectMapperOutputProcessor on write operations - #8420
Conversation
|
@soyuka Can you please take a look at this PR |
edaaefd to
4751d53
Compare
4751d53 to
0cac163
Compare
|
Thanks for the PR and for the clear write-up. I'm going to close it, but not because the diff is wrong in isolation: this exact change was already proposed in #7611 and requested again in #7940, and the answer is the same. Let me restate it here so the reasoning is easy to find. Why
What to do instead With the ObjectMapper integration the #[ApiResource(
shortName: 'Book',
stateOptions: new Options(entityClass: BookEntity::class),
operations: [new Get(), new GetCollection(), new Patch(input: UpdateBook::class)],
)]
#[Map(target: BookEntity::class)]
final class Book { /* full representation */ }
#[ApiResource(
shortName: 'Book',
stateOptions: new Options(entityClass: BookEntity::class),
operations: [
new Post(
uriTemplate: '/books',
input: CreateBook::class,
itemUriTemplate: '/books/{id}', // reuse the Book item IRI for @id
),
],
)]
#[Map(source: BookEntity::class)]
final class BookCreated { /* what POST returns */ }The request is deserialized into If the response is not a projection of the entity at all, use a custom processor with an We'll update the documentation to state explicitly that |
|
Documentation follow-up: api-platform/docs#2321 (clarifies that |
Problem
When using the Symfony ObjectMapper integration (
stateOptions: new Options(entityClass: MyEntity::class)), the read side and write side are inconsistent in how they resolve the target class.On the read side,
ObjectMapperProvidercorrectly respects a configured output class, falling back to the operation class:On the write side,
ObjectMapperOutputProcessorignores theoutputclass entirely and always maps back to$operation->getClass():As a result, a write operation (
POST / PATCH / PUT) that declares a dedicatedoutputclass silently maps the persisted entity back to the resource class instead of the requested output DTO. There is no way to return a different representation from a write than from a read when using ObjectMapper.This is particularly limiting when the resource class itself cannot serve as a valid output representation for writes (for example, when the resource's IRI cannot be generated from the write result), forcing users to fall back to
output: falseand losing the response body altogether.Fix
Mirror the read side so the processor honors the operation's
outputclass when one is defined, falling back to the operation class otherwise:This makes read and write paths symmetric and lets a write operation return a dedicated output DTO via ObjectMapper.
Backward compatibility
Fully backward compatible: when no
outputclass is configured,getOutput()isnulland behavior is unchanged (maps to$operation->getClass()).