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

Dan*_*nko 9 api-platform.com

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

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

{
   "from": "Paris",
   "to": "Berlin"
}
Run Code Online (Sandbox Code Playgroud)

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

{
    "flights": [/* a lot of json data*/],
    "airports": [/* a lot of json data*/],
    "cities": [/* a lot of json data*/],
    .......
}
Run Code Online (Sandbox Code Playgroud)

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

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

第三个我猜它是使用数据提供程序,但我不确定它是否适合我的要求。

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

Nik*_* U. 11

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

由于您想使用 POST HTTP 方法(用于创建资源项),您可以执行以下操作。

1)定义描述搜索字段的类,这将是您的 @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;
}
Run Code Online (Sandbox Code Playgroud)

2)定义将代表输出的DTO

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

namespace App\Dto;

class SearchResult
{
    public $flights;
    public $airports;
    public $cities;
}
Run Code Online (Sandbox Code Playgroud)

3)创建将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
    }
}
Run Code Online (Sandbox Code Playgroud)

这样,您将根据请求收到结果。

  • 嗨@nikita-u,感谢您的回复。我尝试了你的方法并且有效。在等待任何答复的同时,我还设法使用自定义控制器实现另一种方法。它与您的非常相似,但我没有创建数据持久化,而是创建了自定义操作。我不知道哪一个更好,它们的优点和缺点是什么。但它们似乎都是解决方法。这可能是 API 平台特有的。不管怎样,谢谢你的帮助。 (2认同)