当前位置: 首页 > 知识库问答 >
问题:

如何在 api 平台中通过过滤实现自定义项目获取endpoint?

咸亦
2023-03-14

我正在开发一个symfony/api平台应用程序,允许用户跟踪体育比赛。我的实体看起来像这样(为简洁起见,将其缩短):

用户.php

class User implements UserInterface
{
    // ...

    /**
     * @ORM\OneToMany(targetEntity=MatchPlayer::class, mappedBy="user")
     */
    private $matches;

    // ...
}

MatchPlayer.php

class MatchPlayer
{
    // ...

    /**
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy="matches")
     * @ORM\JoinColumn(onDelete="SET NULL")
     */
    private $user;

    /**
     * @ORM\ManyToOne(targetEntity=Match::class, inversedBy="players")
     */
    private $playedMatch;

    /**
     * @ORM\ManyToOne(targetEntity=Position::class, inversedBy="matches")
     */
    private $position;

    // ...
}

匹配.php

class Match
{
    // ...

    /**
     * @ORM\Column(type="smallint")
     * @Groups({"match:read"})
     */
    private $outcome;

    /**
     * @ORM\ManyToOne(targetEntity=Sport::class, inversedBy="matches")
     */
    private $sport;

    /**
     * @ORM\OneToMany(targetEntity=MatchPlayer::class, mappedBy="playedMatch", cascade={"persist", "remove"})
     */
    private $players;

    // ....
}

因此,在我的模型中,用户可以与许多匹配项相关联,并且匹配项可以通过胶水表与许多用户相关联,该粘附表还保存了用户所处的位置。

现在我想公开一个带有api平台的endpoint,如/api/用户/{id}/统计/api/统计/{userId},它动态获取数据并显示用户在哪个运动中打了多少场比赛,在什么位置,以及用户赢了/平了/输了多少场比赛。理想情况下,endpoint将允许按运动过滤,看起来像/api/用户/{id}/统计?运动[]=足球

因为这些统计信息不会作为实体持久化到数据库中,所以我尝试了一种类似于没有任何路由留档页面的公开模型的方法。我创建了一个统计实体,如下所示:

/**
 * @ApiResource(
 *     collectionOperations={},
 *     itemOperations={
 *          "get"={
 *              "controller"=NotFoundAction::class,
 *              "read"=false,
 *              "output"=false,
 *          },
 *     }
 * )
 */
class Statistic
{
    /**
     * @var User
     * @ApiProperty(identifier=true)
     */
    public $user;

    /**
     * @var Position[]|null
     */
    public $position = [];

    /**
     * @var Sport[]|null
     */
    public $maps = [];

    /**
     * @var int
     */
    public $wins = 0;

    /**
     * @var int
     */
    public $ties = 0;

    /**
     * @var int
     */
    public $losses = 0;
}

并向用户实体添加了自定义操作:

 * @ApiResource(
 *    ...
 *     itemOperations={
 *          "get_statistic"={
 *              "method"="GET",
 *              "path"="/users/{id}/statistics",
 *          }
 *     },
 *    ...
 */

然而,我不确定如何通过运动、位置和赢/平/输实现过滤。据我所知,“普通”过滤器不起作用,因为它只适用于集合的get操作。

如果这是可能的,我将如何在我的 API 中实现它?我已经尝试了自定义数据提供程序和控制器,但我无法在任一解决方案中获取过滤器查询参数,并且“普通”过滤器(如 SearchFilter 中内置的 api 平台)不起作用,因为它仅适用于集合的 get 操作,并且我正在处理一个项目。


共有1个答案

晏沈义
2023-03-14
匿名用户

这当然是可能的,但取决于你的选择,你需要做更多的工作来获得期望的结果。

我将使用自定义操作,因为这更容易解释,而且我已经有了一些代码示例。

要获取过滤所需的信息,您需要使用较低级别的方法。您错过的关键部分是API平台构建在Symfony之上,因此您可以使用请求(用于自定义操作)或请求堆栈(用于数据提供者)来获取过滤器。

此外,为了确保 API 平台知道如何序列化您输出的数据(统计对象),您需要使用 DTO。

代码如下所示:

在您的实体上,我们添加自定义操作类并将输出指定为 Statistics 类:

 * @ApiResource(
 *    ...
 *     itemOperations={
 *          "get_statistics"={
 *              "method"="GET",
 *              "path"="/users/{id}/statistics",
 *              "controller"=UserStatsAction::class,
 *              "input"=Statistics::class
 *          }
 *     },
 *    ...
 */

自定义操作代码示例:

final class UserStatsAction
{
    private $em;


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

    public function __invoke(Request $request)
    {
        $id = $request->get('id');
        $repository = $this->em->getRepository(User::class);
        if(!($user = $repository->find($id))) {
            throw new NotFoundHttpException();
        }

        $sports = $request->query->get('sport', []);
        $outcome = $request->query->get('outcome');

        // Optional: validate your filter data
        $validator = Validation::createValidator();
        $context = $validator->startContext();
        $context->atPath('sports')->validate($sports, [
            new Assert\Choice([
                'choices' => ['football', 'basketball'],
            ]),
        ]);
        $context->atPath('outcome')->validate($outcome, [
            new Assert\Choice([
                'choices' => ['win', 'loose', 'tie'],
            ]),
        ]);
        $violations = $context->getViolations();

        if (0 !== count($violations)) {
            throw new ValidationException($violations);
        }

        // I'll assume you are hnadiling empty/nulls value properly inside this method
        // and return all the stats if 
        $results = $repository->getStatistics($sports, $outcome);

        // For this to work, you'll need to set a DTO for your stats
        return $results;
    }
}

我使用Request作为自定义操作的参数,而不是User实体。我的示例中有一些您可能不需要的代码,例如从存储库中提取用户或验证过滤器(但我确实鼓励用户输入清理/验证)。

一个重要的提法:API 平台不鼓励自定义操作,你将失去 GraphQL 支持。如果你需要 GraphQL,同样的结果可以用 DataProvider 完成,但这是一个更高级的设置,我需要模拟你应用程序的某些部分来弄清楚它。

希望这有帮助。

更新:

要使过滤器正常工作,您还需要更新OpenAPI/Swagger配置,正如托比亚斯·英戈尔德在下面的评论中指出的那样。

您可以使用 PHP 并创建规范化程序来执行此操作,如文档的覆盖 OpenAPI 配置部分中所述。

这也可以通过扩展< code>APIResource注释来实现,下面是一个示例:

 * @ApiResource(
 *     ...
 *     collectionOperations={
 *          "post",
 *          "get"={
 *              "openapi_context"={
 *                  "parameters"={
 *                      {
 *                          "name": "<query_string_param_name>",
 *                          "type": "string",
 *                          "in": "query",
 *                          "required": false,
 *                          "description": "description",
 *                          "example": ""
 *                      }
 *                  }
 *              }
 *          }
 *     }
 *     ...
 *  })

我发现这种方法更容易使用,但是没有文档记录。我是根据我的OpenAPI规范知识和官方文档中配置接收上传文件的实体的例子推断出来的。

 类似资料:
  • 本文向大家介绍django 自定义过滤器的实现,包括了django 自定义过滤器的实现的使用技巧和注意事项,需要的朋友参考一下 自定义模版过滤器 虽然DTL给我们内置了许多好用的过滤器。但是有些时候还是不能满足我们的需求。因此Django给我们提供了一个接口,可以让我们自定义过滤器,实现自己的需求。 模版过滤器必须要放在app中,并且这个app必须要在INSTALLED_APPS中进行安装。然后再

  • 我正在Google云平台上运行Hadoop集群,使用Google云存储作为持久数据的后端。我能够从远程机器ssh到主节点,并运行hadoop fs命令。无论如何,当我尝试执行以下代码时,我得到了一个超时错误。 密码 执行hdfs.exists()命令时,我得到一个超时错误。 错误 组织。阿帕奇。hadoop。网ConnectTimeoutException:来自gl051-win7/192的调用。

  • 演示在网关追加一个header public class CustomFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { // 演示在网关追加heade

  • SOFARPC 提供了一套良好的可扩展性机制,为各个模块提供 SPI 的能力。 SOFARPC 对请求与响应的过滤链处理方式是通过多个过滤器 Filter 来进行具体的拦截处理,该部分可由用户自定义 Filter 扩展,自定义 Filter 的执行顺序在内置 Filter 之后。具体方式如下: Bolt Filter 新建自定义 Filter 。 public class CustomFilter

  • 我必须滚动到recyclerview中的特定项目。 首先,我需要通过匹配从活动传递给适配器的字符串来获取项的位置。然后聚焦到该项目的位置。 例如,在适配器中有一个存储了EventID的TextView,我将把一个字符串从activity传递给适配器,使其与EventID匹配,然后获取该项目的位置,并将焦点/滚动设置到该特定位置。我在适配器中定义了一个方法来获取项的位置,但我不知道如何从活动中调用它