core icon indicating copy to clipboard operation
core copied to clipboard

[2.7.0] ApiResource output data-transformers are not called in a relationship context anymore

Open ambroisemaupate opened this issue 3 years ago • 2 comments

API Platform version(s) affected: 2.7.0

Description

I've registered an ApiResource only for read item operation:

App\GeneratedEntity\NSGalleryBlock:
    iri: GalleryBlock
    shortName: GalleryBlock
    collectionOperations: {  }
    attributes:
        output: App\Api\Dto\GalleryBlockOutput
    itemOperations:
        get:
            method: GET
            normalization_context:
                groups:
                    - nodes_sources
                    - urls
                    - tag_base
                    - translation_base
                    - document_display

Then I've got another resource which will store relationships to differents kind of resources:

App\Model\WebResponse:
    collectionOperations: {}
    itemOperations:
        getByPath:
            method: 'GET'
            path: '/web_response_by_path'
            read: false
            controller: RZ\Roadiz\CoreBundle\Api\Controller\GetWebResponseByPathController
            pagination_enabled: false
            normalization_context:
                pagination_enabled: false
                groups:
                    - get
                    - web_response
                    - nodes_sources
                    - urls
                    - tag_base
                    - translation_base
                    - document_display

Direct resource call

If I call item operation for NSGalleryBlock resource directly, for example:

GET /api/gallery_blocks/2811

then GalleryBlockOutputDataTransformer is automatically called to hydrate GalleryBlockOutput DTO. image

media property is populated by GalleryBlockOutputDataTransformer

Indirect resource call

If I call item operation for WebResponse resource, for example

GET /api/web_response_by_path?path=/path-to-my-web-response

and this WebResponse holds a relationship to /api/gallery_blocks/2811 then GalleryBlockOutputDataTransformer is not called anymore! Only WebResponseDataTransformer is called because it is the main resource class for this operation. image

media property cannot be populated by GalleryBlockOutputDataTransformer, it remains empty

Possible Solution

This worked on 2.6.x because InputOutputMetadataTrait was getting output class from current normalizing object metadata, not the current operation context.

https://github.com/api-platform/core/blob/2.6/src/Serializer/InputOutputMetadataTrait.php#L48

ambroisemaupate avatar Sep 20 '22 10:09 ambroisemaupate

I don't think that this was ever supported and it looks like you should use a normalizer instead of a data transformer. I'd strongly advise updating to 3.0 where this'd be way easier to handle.

soyuka avatar Sep 20 '22 11:09 soyuka

These examples purpose is to create custom DTOs from a model or Doctrine entities.

As I understood from Api-platform documentation, Normalizers are contextual to a format (JSON, XML, JSON-LD). Then I find it cumbersome to use Normalizers to create DTOs which should be format-agnostic, it is just creating temporary data, not presentation (...then normalizers serialize my DTOs).

Moreover, Normalizers are too low-level for injecting non-scalar properties to objects, especially properties that store objects or Resources as well. My custom normalizer may depend on serializer service to serialize relationships and this could lead to cyclic dependency (I tried this way before using DTOs).

In the previous example, NSGalleryBlock resource was decorated to inject media property at runtime. But this media property holds an array<Document> resource which must be serialized as well and also use a DocumentOutput DTO. Up to api-platform/core 2.6.x, DataTransformer worked very well at creating DTOs in cascade (each time Serializer meets a new Resource that is configured to be output with a DTO) which sounds logical and was a bit magic too :smile:.

But for the moment I will freeze my dependencies to api-platform/core:~2.6.8. Until I figure out how to migrate my whole system to 3.0

ambroisemaupate avatar Sep 20 '22 14:09 ambroisemaupate

Use a Provider / Processor. You shouldn't need a DataTransformer and yes Normaliers may be more appropriate to change your output at will. You can make a normalizer non-format specific.

soyuka avatar Sep 29 '22 08:09 soyuka