Ben
Ben

Reputation: 915

Api-Platform: using PUT for creating resources

I would like to use the PUT method for creating resources. They are identified by an UUID, and since it is possible to create UUIDs on the client side, I would like to enable the following behaviour:

It's possible to achieve this by implementing an ItemDataProviderInterface / RestrictedDataProviderInterface.

However, my resource is actually a subresource, so let's say I want to create a new Book which references an existing Author.

My constructor looks like this:

/**
 * Book constructor
 */
public function __construct(Author $author, string $uuid) {
    $this->author = $author;
    $this->id = $uuid;
}

But I don't know how to access the Author entity (provided in the request body) from my BookItemProvider.

Any ideas?

Upvotes: 3

Views: 2189

Answers (1)

Nek
Nek

Reputation: 3125

In API Platform many things that should occur on item creation is based on the kind of request it is. It would be complicated to change.

Here are 2 possibilities to make what you want.

First, you may consider to do a custom route and use your own logic. If you do it you will probably be happy to know that using the option _api_resource_class on your custom route will enable some listeners of APIPlaform and avoid you some work.

The second solution, if you need global behavior for example, is to override API Platform. Your main problem for this is the ReadListener of ApiPlatform that will throw an exception if it can't found your resource. This code may not work but here is the idea of how to override this behavior:

class CustomReadListener
{
    private $decoratedListener;

    public function __construct($decoratedListener)
    {
        $this->decoratedListener = $decoratedListener;
    }

    public function onKernelRequest(GetResponseEvent $event)
    {
        try {
            $this->decoratedListener->onKernelRequest($event);
        } catch (NotFoundHttpException $e) {
            // Don't forget to throw the exception if the http method isn't PUT 
            // else you're gonna break the 404 errors
            $request = $event->getRequest();

            if (Request::METHOD_PUT !== $request->getMethod()) {
                throw $e;
            }

            // 2 solutions here:

            // 1st is doing nothing except add the id inside request data
            // so the deserializer listener will be able to build your object


            // 2nd is to build the object, here is a possible implementation

            // The resource class is stored in this property
            $resourceClass = $request->attributes->get('_api_resource_class');

            // You may want to use a factory? Do your magic.
            $request->attributes->set('data', new $resourceClass());
        }
    }
}

And you need to specify a configuration to declare your class as service decorator:

services:
    CustomReadListener:
        decorate: api_platform.listener.request.read
        arguments:
            - "@CustomReadListener.inner"

Hope it helps. :)

More information:

Upvotes: 3

Related Questions