API 平台 - 我应该使用哪种方法来创建没有实体的自定义操作

API Platform - Which approach should I use for creating custom operation without entity

我是 API 平台的新手。我认为这很棒,但我找不到任何示例如何创建不基于任何实体的自定义端点。有很多基于实体的示例,通常它们都是关于 CRUD 的。但是自定义操作呢?

我需要使用一些与任何实体无关的自定义参数通过数据库创建自定义搜索。 例如。我想收到 POST 类似这样的请求:

{
   "from": "Paris",
   "to": "Berlin"
}

此数据未保存到数据库中,我还没有实体。 在我收到这些数据之后,应该有很多业务逻辑,包括通过大量数据库表进行数据库查询以及从外部来源获取数据。 然后,在业务逻辑完成后,我想 return 返回结果,该结果也是自定义的并且与任何实体都不相关。 例如

{
    "flights": [/* a lot of json data*/],
    "airports": [/* a lot of json data*/],
    "cities": [/* a lot of json data*/],
    .......
}

所以,我想我不是唯一做类似事情的人。但我真的找不到如何做到这一点的解决方案或最佳实践。 在文档中,我发现了至少三种方法,但我无法实施其中的 none 种方法。 最好的,我想最适合我的是使用自定义操作和控制器。但是文档说不推荐这个。另外我认为我应该使用 DTO 进行请求和响应,但是对于这种方法我不确定我是否可以使用它们。

我发现的第二个是使用数据传输对象,但这种方法需要一个实体。根据文档,我应该使用 DTO 和 DataTransformers 将 DTO 转换为实体。但我不需要实体,我不需要将它保存到数据库。我只想自己处理收到的 DTO。

第三个我猜是使用Data Provider,但我不确定它是否适合我的要求。

所以,主要问题是我应该使用哪种方法或最佳实践来实现与任何实体无关的自定义操作。将 DTO 用于请求和响应将非常有用。

您不是被迫使用实体。标有 @ApiResource 注释的 类 可能不是实体。实际上,如果您的应用程序比基本的 CRUD 更智能,您应该避免将实体标记为 ApiResource。

既然您想使用 POST HTTP 方法(用于创建资源项),您可以这样做。

1) 定义 class 描述搜索字段,这将是您的 @ApiResource

<?php
// src/ApiResource/Search.php 

namespace App\ApiResource;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Action\NotFoundAction;
use ApiPlatform\Core\Annotation\ApiProperty;
use App\Dto\SearchResult;

/**
 * @ApiResource(
 *     itemOperations={
 *         "get"={
 *             "controller"=NotFoundAction::class,
 *             "read"=true,
 *             "output"=false,
 *         },
 *     },
 *     output=SearchResult::class
 * )
 */
class Search
{
    /**
     * @var string
     * @ApiProperty(identifier=true)
     */
    public $from;

    /** @var string */
    public $to;
}

2) 定义代表输出的 DTO

<?php
// src/Dto/SearchResult.php

namespace App\Dto;

class SearchResult
{
    public $flights;
    public $airports;
    public $cities;
}

3) 创建 class 来实现 DataPersisterInterface 来处理业务逻辑。 它会被框架调用,因为你提出 POST 请求。

<?php
// src/DataPersister/SearchService.php

declare(strict_types=1);

namespace App\DataPersister;

use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use App\Dto\SearchResult;
use App\ApiResource\Search;

final class SearchService implements DataPersisterInterface
{
    public function supports($data): bool
    {
        return $data instanceof Search;
    }

    public function persist($data)
    {
        // here you have access to your request via $data
        $output = new SearchResult();
        $output->flights = ['a lot of json data'];
        $output->airports = ['a lot of json data'];
        $output->cities = ['inputData' => $data];
        return $output;
    }

    public function remove($data)
    {
        // this method just need to be presented
    }
}

这样您将收到基于请求的结果。