it-bens/api-platform-resource-actions-bundle

可以将操作添加到API Platform操作中,以反序列化负载到命令对象并将其传递给API Platform工作流程的Bundle。

dev-master 2022-09-20 09:36 UTC

This package is auto-updated.

Last update: 2024-09-15 13:41:33 UTC


README

Maintenance Status Tests codecov

动机

让我们从Symfony & API Platform 👷开始

Symfony & API Platform是一个优秀的组合,可以轻松地为CRUD应用程序创建REST API。由于设计限制,REST API的灵活性有限。由于操作是通用的,因此很难描述更具体的操作。

更新模型的典型方式是应用完整的更新操作(REST PUT)或应用部分更新(REST PATCH)。通常,模型更新与某些要求或副作用相关联。它们的实现往往变得非常复杂。

解决此问题的一种方法是在模型上应用命令对象。命令实现可以分离出来以检查要求或触发副作用,而无需编写越来越长的方法。不幸的是,这个概念与API Platform实现的REST API的简单性相冲突。可以实施自定义操作,但这会产生大量的样板代码。API Platform还可以填充通用的更新DTO并通过总线发送它。但此DTO的处理程序必须创建命令对象并应用它们。这将需要更多或更少的复杂逻辑,这很难维护和测试。

API Platform资源操作来拯救!🦸

在REST API环境中,提到的“模型”可以称为“资源”。操作指的是提到的命令,但更通用。

此Bundle提供通过配置将操作附加到API Platform资源操作的能力。内部使用通用的更新DTO,稍后将其解包以创建所需的操作。处理后,操作对象将通过总线分发。它可以像来自任何其他来源(如表单请求或控制台命令)一样进行处理。这有助于将API逻辑代码保留在模型之外,并使它们对数据源更具不可知性。

安装

此Bundle可以通过Composer安装

composer require it-bens/api-platform-resource-actions-bundle

如果您使用Symfony Flex,则Bundle将自动启用(但必须手动配置)。对于旧应用程序,请在Kernel类中启用它。

操作配置

要注册操作,需要三个要素:一个DTO、一个配置条目和API Platform中适当的资源配置。

假设存在以下命令DTO

namespace TheNamespace;

class AppendToPropertyOne {
    public function __construct(
        private TheNamespace\TheEntity $entity, 
        private string $toAppend
    ) {}
}

命令包含将应用于其实体的实体以及一个额外的属性。

操作可以通过Bundle配置和/或类属性定义(和注册)。两种来源都可以结合使用,但如果为资源定义了多次操作,则将引发异常。

此Bundle将检查在定义中使用的资源是否已在API Platform中注册。

使用配置文件定义操作

itb_api_platform_resource_actions:
   resources:
      TheNamespace\TheEntity:
         increase-property-two:
            command_class: TheNamespace\AppendToPropertyOne
            description: Appends a string to property one.

“resource”键指向API Platform资源。子键表示操作名称(它将不会规范化为snake-case)。描述是可选的,并将用于OpenAPI文档(空描述将导致异常)。

使用属性定义操作

namespace TheNamespace;

use ApiPlatform\Core\Annotation\ApiResource;
use ITB\ApiPlatformResourceActionsBundle\Attribute\ResourceAction;

#[ApiResource]
#[ResourceAction(actionName: 'increase-property-two', commandClass: AppendToPropertyOne::class, description: 'Appends a string to property one.')]
class TheEntity {
    ...
}

“description”参数是可选的,也可以为null。

通常,此Bundle将在所有注册的类中搜索该属性。可以通过它们的命名空间限制考虑的类。

itb_api_platform_resource_actions:
  resources:
    TheNamespace\TheEntity:
      increase-property-two:
        command_class: TheNamespace\AppendToPropertyOne
        description: Appends a string to property one.

API Platform的配置

到目前为止,这个包什么都不会做(除了注册一些服务)。在配置了使用此包提供的控制器和请求DTO的API Platform操作后,可以使用这些操作。还考虑了子命名空间。

itb_api_platform_resource_actions:
   attribute_namespaces: [ TheNamespace ]

当然,也可以通过属性来配置。

use ITB\ApiPlatformResourceActionsBundle\Request\Request;
use ITB\ApiPlatformResourceActionsBundle\Controller\Controller;
use ITB\ApiPlatformResourceActionsBundle\Attribute\ResourceAction;

#[ApiResource(itemOperations: [
    'patch' => [
        'input': Request::class,
        'controller': Controller::class
    ]
])]
#[ResourceAction(...)]
class TheEntity {
    ...
}

ℹ️ 如果要在那里处理命令,必须启用 messenger 选项。也可以使用自定义数据持久器。

通过控制台列出配置的操作

配置后,可以通过Symfony控制台显示资源操作,并通过资源进行过滤。

php bin/console itb:api-platform-resource-actions:list-actions
# or with filter by resource
php bin/console itb:api-platform-resource-actions:list-actions --resource="TheNamespace\TheEntity"

控制台将显示一个包含“API Platform资源”、“操作名称”、“命令类”和“描述”列的表格。

操作验证

此包可以手动验证创建的命令。API Platform验证输入,但在此阶段,输入对象是通用的。因此,命令在控制器中进行验证。默认情况下是禁用的,可以在包配置中启用。

itb_api_platform_resource_actions:
  validate_command: true

Open API 文档

好吧,不是!但在这里:也许?🤔

API Platform可以自动创建OpenAPI文档。此包通过如这里所述的装饰器钩入此过程:https://api-platform.com/docs/core/openapi/

它将在操作描述中添加一个表格,如下所示

OpenAPI documentation for operation with actions

Payload列显示了命令类的属性。如果类包含与API Platform资源相同类型的属性,它将被从列表中删除(因为这很可能是命令将要应用的对象)。

内部流程

此过程与API Platform紧密耦合,并包含几个步骤

  1. API Platform将原始数据规范化为该包的通用Request DTO。
  2. API Platform调用RequestTransformer作为数据转换器。它将资源类和资源对象注入到Request对象的负载中。
  3. API Platform使用ActionRequestValidator验证Request对象。它检查操作是否已注册为该资源,并通过使用CommandFactory检查负载是否包含必要的数据。
  4. API Platform/路由器调用Controller并将Request对象传递给它。
  5. 控制器将负载数据规范化为命令。
  6. 控制器(如果启用)使用API Platform验证器验证命令。
  7. 控制器将命令返回到默认的API Platform流程。

当前限制

具有两种资源类型的属性命令

此包可以处理创建一个具有两种相关资源类型的属性命令。它使用ITB ReflectionConstructorhttps://github.com/it-bens/reflection-constructor),可以在将资源对象注入到负载中进行后续规范化时使用忽略属性名称的列表。

如果类中不是所有属性都是必需的或由构造函数设置的,则此过程可能会有问题。(这本来也是一种不好的做法)。

此外,负载属性的OpenAPI文档忽略了与资源相同类型的任何属性。在具有这种类型两个属性的情况下,包本身将按描述工作,但文档将不完整。

双重去规范化

Request类被API Platform作为通用类进行验证。《ActionRequestValidator》尝试将负载去规范化为命令对象,并捕获去规范化异常。如果验证通过,则在控制器中再次进行去规范化。为了最小化这种双重去规范化对性能的影响,应使用序列化器缓存。

无构造函数的命令

RequestTransformer 将查找具有资源类型的构造函数参数。如果命令/DTO没有构造函数,则假定命令不需要资源对象。

此包使用默认的 Symfony 序列化器(由框架创建)进行反序列化。因此,此过程仅限于 Symfony 序列化器的功能。

对每个资源执行多个操作

API Platform 通过资源配置识别操作的执行。如果将 'input' 键设置为 Request 并且控制器设置为(此包的)Controller,则假定(指定资源的)操作将使用这些操作。如果配置了多个具有相同资源的操作,则仅使用第一个。

API Platform 仍会调用控制器执行任何其他操作,并且它将无法找到相应的操作。

相关包/包

由于 API Platform 总是使用默认的代理来分发命令作为消息,因此 Message Bus Redirect Bundle(https://github.com/it-bens/message-bus-redirect-bundle)非常有用。

贡献

我真的很高兴软件开发社区热爱开源,就像我一样!♥

这就是为什么我感激每一个被打开的问题(最好是建设性的)和每一个提供其他或更好代码的拉取请求。

你们都太棒了!