Api-Platform: using PUT for creating resources

旧巷老猫 提交于 2019-12-11 04:08:03

问题


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:

  • on PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 if this resource exists, update it
  • on PUT /api/myresource/4dc6efae-1edd-4f46-b2fe-f00c968fd881 if this resource does not exist, create it

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?


回答1:


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:

  • Information about event dispatcher and kernel events: http://symfony.com/doc/current/components/event_dispatcher.html
  • ApiPlatform custom operation: https://api-platform.com/docs/core/operations#creating-custom-operations-and-controllers
  • Symfony service decoration: https://symfony.com/doc/current/service_container/service_decoration.html


来源:https://stackoverflow.com/questions/49767594/api-platform-using-put-for-creating-resources

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!