Skip to content

Unable to generate an IRI with GetCollection or Post when subresource uses output DTO #7211

Closed
@fmata

Description

@fmata

API Platform version(s) affected: 4.1.12

Description
I only expose DTO with output and custom providers/processors. All works fine except GetCollection and Post operations on subresource : the IRI generation does not work in this context although it works with Get and Put operations.

How to reproduce
I have a resource App\EmployeeOutput (not a Doctrine entity) mapped as follow

resources:
    App\EmployeeOutput:
        operations:
            -
                class: ApiPlatform\Metadata\Get
                uriTemplate: /company/{id}/employees/{employee}
                uriVariables:
                    id:
                        fromClass: App\Company
                    employee:
                        fromClass: App\Employee
                output: App\EmployeeOutput
            -
                class: ApiPlatform\Metadata\GetCollection
                uriTemplate: /company/{id}/employees
                uriVariables:
                    id:
                        fromClass: App\Company
                        toProperty: company
                provider: App\EmployeeCollectionProvider
                output: App\EmployeeOutput
            -
                class: ApiPlatform\Metadata\Post
                uriTemplate: /company/{id}/employees
                uriVariables:
                    id:
                        fromClass: App\Company
                        toProperty: cra
                input: App\AddEmployeeInput
                processor: App\AddEmployeeProcessor
                output: App\EmployeeOutput

My DTO

readonly class EmployeeOutput
{
    public function __construct(
        private string $id,
        public string $firstName,
        public string $lastName,
    ) {
    }

    public function id(): string
    {
        return $this->id;
    }
}

And my Doctrine entities

class Company
{
    private Id $id;
    /** @var iterable<int, Employee> */
    private iterable $employees = [];

    // ...
}

class Employee
{
    private Id $id;
    private Company $company;

    // ...
}

Company Doctrine mapping

   <one-to-many field="employees" target-entity="App\Employee" mapped-by="company" orphan-removal="true">
      <cascade>
        <cascade-persist />
        <cascade-remove />
      </cascade>
    </one-to-many>

Employee Doctrine mapping

    <many-to-one field="company" target-entity="App\Company" inversed-by="employees">
      <join-column nullable="false" on-delete="CASCADE"/>
    </many-to-one>

In my custom collection provider EmployeeCollectionProvider, I must call the decorated @api_platform.doctrine.orm.state.collection_provider with $operation->withClass(Employee::class) to get the result. The query generated is correct and the results are ok.

The problem is on the normalization side at src/Metadata/IdentifiersExtractor.php:135 : Unable to generate an IRI for the item of type "App\EmployeeOutput".

IdentifiersExtractor::getIdentifierValue() is called with

  • $item : App\EmployeeOutput instance
  • $class : "App\Company"
  • $property : "id"
  • $parameterName : "id"
  • $toProperty : null

Same problem with Post operation.

I tried everything but I suspect that subresource and DTO are not very well supported when the subresource ID is not in the uriTemplate, or I messed up with the config.

If I expose my entities without using output, it works fine.

How can I achieve the generation of IRI in the GetCollection or Post operation context ?

Possible Solution
🤷

Additional Context
The Get and Put operations work good and the IRI are ok.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions