Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
682 views
in Technique[技术] by (71.8m points)

api platform.com - Operation without entity

I've been looking for a solution for a while but none of the one I find really allows me to do what I want. I would just like to create routes that don't necessarily require an entity or id to be used. Can you help me the documentation is not clear to do this.

Thank you beforehand.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As you can read in the General Design Considerations, just make an ordinary PHP class (POPO). Give it an ApiResource annontation like this:

 * @ApiResource(
 *     collectionOperations={
 *         "post"
 *     },
 *     itemOperations={}
 * )

Make sure the folder your class is in is in the paths list in api/config/packages/api_platform.yaml. There usually is the following configuration:

api_platform:
    mapping:
        paths: ['%kernel.project_dir%/src/Entity']

You should add your path if your class is not in the Entity folder.

Api Platform will expect json to be posted and try to unserialize it into an instance of your class. Make a custom DataPersister to process the instance, for example if your class is AppApiCommandDoit:

namespace AppDataPersister;

use ApiPlatformCoreDataPersisterContextAwareDataPersisterInterface;
use AppApiCommandDoit;
use AppApiResultDoitResult;

final class DoitDataPersister implements ContextAwareDataPersisterInterface
{
    public function supports($data, array $context = []): bool
    {
        return $data instanceof Doit;
    }

    public function persist($data, array $context = [])
    {
        // code to process $data

        $result = new DoitResult();
        $result->description = 'Hello world';
        return $result;
    }

    public function remove($data, array $context = [])
    {
      // will not be called if you have no delete operation
    }
}

If you need Doctrine, add:

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

See Injecting Extensions for how to use it.

Notice that the result returned by ::persist is not an instance of Doit. If you return a Doit api platform will try to serialize that as the result of your operation. But we have marked Doit as an ApiResource so (?) api platform looks for an item operation that can retrieve it, resulting in an error "No item route associated with the type AppApiCommandDoit". To avoid this you can return any object that Symfonies serializer can serialize that is not an ApiResource. In the example an instance of DoitResult. Alternatively you can return an instance of SymfonyComponentHttpFoundationResponse but then you have to take care of the serialization yourself.

The post operation should already work, but the swagger docs are made from metadata. To tell api platform that it should expect a DoitResult to be returned, change the @ApiResource annotation:

 *     collectionOperations={
 *         "post"={
 *             "output"=DoitResult::class
 *         }
 *     },

This will the add a new type for DoitResult to the swagger docs, but the descriptions are still wrong. You can correct them using a SwaggerDecorator. Here is one for a 201 post response:

namespace AppSwagger;

use SymfonyComponentSerializerNormalizerNormalizerInterface;

final class SwaggerDecorator implements NormalizerInterface
{
    private $decorated;

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

    public function normalize($object, string $format = null, array $context = [])
    {
        $summary = 'short explanation about DoitResult';
        $docs = $this->decorated->normalize($object, $format, $context);
        $docs['paths']['/doit']['post']['responses']['201']['description'] = 'Additional explanation about DoitResult';

        $responseContent = $docs['paths']['/doit']['post']['responses']['201']['content'];
        $this->setByRef($docs, $responseContent['application/ld+json']['schema']['properties']['hydra:member']['items']['$ref'],
            'description', $summary);
        $this->setByRef($docs, $responseContent['application/json']['schema']['items']['$ref'],
            'description', $summary);

        return $docs;
    }

    public function supportsNormalization($data, string $format = null)
    {
        return $this->decorated->supportsNormalization($data, $format);
    }

    private function setByRef(&$docs, $ref, $key, $value)
    {
        $pieces = explode('/', substr($ref, 2));
        $sub =& $docs;
        foreach ($pieces as $piece) {
            $sub =& $sub[$piece];
        }
        $sub[$key] = $value;
    }
}

To configure the service add the following to api/config/services.yaml:

'AppSwaggerSwaggerDecorator':
    decorates: 'api_platform.swagger.normalizer.api_gateway'
    arguments: [ '@AppSwaggerSwaggerDecorator.inner' ]
    autoconfigure: false

If your post operation is not actually creating something you may not like the 201 response. You can change that by specifying the response code in the @ApiResource annotation, for example:

 *     collectionOperations={
 *         "post"={
 *             "output"=DoitResult::class,
 *             "status"=200
 *         }
 *     },

You may want to adapt the SwaggerDecorator accordingly.

Creating a "get" collection operation is similar, but you need to make a DataProvider instead of a DataPersister. The chapter9-api branch of my tutorial contains an example of a SwaggerDecorator for a collection response.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...