Search code examples
symfonyapi-platform.com

Change default GET operation used by API platform for GetCollection items


I have a custom Get operation on the user entity "/users/me" which is my first operation. Hence, it matches sooner than the "/users/{id}" operation used to retrieve a specific user.

But now, every user in GetCollection "/users" have {"@id": "/users/me"}

If I swap the order of the "/users/me" with "/users/{id}" then the "/users" works fine, but then for "/users/me" I get a not found exception because a user with id "me" does not exist, obviously.

By default, API Platform uses the first Get operation defined to generate the IRI of an item and the first GetCollection operation to generate the IRI of a collection.

https://api-platform.com/docs/core/controllers/

Is there any way to override the default behavior for a certain collection operation?

I have tried naming my routes and tried to figure out a way from Get operation class attributes which attribute can override the "first operation by default" but I have failed to find it in the documentation or in the code.

Do I have any other option to keep "/users/me" or do I have to forget about using "/users/me" as an endpoint and rather use something else?

Thank you

#[ApiResource(
    operations: [
        new Get(
            uriTemplate: '/users/me',
            controller: UserBasicInfo::class,
            description: 'Get the current user basic information.',
            normalizationContext: [
                'groups' => ['user:me:get'],
            ],
            security: "is_granted('IS_AUTHENTICATED_FULLY')",
            securityMessage: 'Access denied.',
            read: false,
            write: false,
            name: "api_users_get_self",
        ),
        new Get(

            normalizationContext: [
                'groups' => ['user:get'],
            ],
            security: "is_granted('IS_AUTHENTICATED_FULLY')",
            securityMessage: 'Access denied.',
            read: true,
            name: "api_users_get_item",
        ),
        new GetCollection(
            normalizationContext: [
                'groups' => ['user:get:collection'],
            ],
            security: "is_granted('IS_AUTHENTICATED_FULLY')",
            securityMessage: 'Access denied.',
        ),
        ...
    ],
    paginationEnabled: true,
)]

Solution

  • The solution posted by @Aymeric works fine however it has some drawbacks on the documentation generation side.

    I was experimenting and came across an idea, that if I use the /users/{id} operation first so it works as default, but add the uriTemplate explicitly and set the requirement for {id} to be a digit, then the /users/{id} should not match for the /users/me and it should continue for the custom /users/me operation.

    • Responses for Collection and Get are correct
    • Response for /users/me is correct
    • Auto-generated documentation is correct
    new Get(
        uriTemplate: '/users/{id}',
        requirements: ['id' => '\d+'],
        normalizationContext: [
            'groups' => ['user:get'],
        ],
    ),
    new Get(
        uriTemplate: '/users/me',
        controller: UserBasicInfo::class,
        description: 'Get the current user basic information.',
        normalizationContext: [
            'groups' => ['user:me:get'],
        ],
    ),
    new GetCollection(
        normalizationContext: [
            'groups' => ['user:get:collection'],
        ],
    ),