wwwision/graphql

使用 Flow 创建 GraphQL 端点的基本包

资助包维护!
bwaidelich
Paypal

安装次数: 13,396

依赖项: 2

建议者: 0

安全性: 0

星标: 20

关注者: 7

分支: 16

开放问题: 0

类型:neos-package

5.4.0 2024-03-12 17:47 UTC

README

使用 Neos 和 Flow 简单地创建 GraphQL API,请访问 https://www.neos.io/https://flow.neos.io/

背景

此包是一组小工具,可简化使用 Neos 和 Flow 提供 GraphQL 端点。它是对 webonyx 的 PHP 版本的包装,可以自动从 PHP 代码生成模式(使用 wwwision/types)以及易于配置的 PSR-15 兼容 HTTP 中间件。

使用方法

通过 composer 安装

composer require wwwision/graphql

简单教程

创建一个包含至少一个具有 Query 属性的公共方法的类(更多详细信息请参阅 wwwision/types-graphql

// YourApi.php
<?php
namespace Your\Package;

use Neos\Flow\Annotations as Flow;
use Wwwision\TypesGraphQL\Attributes\Query;

#[Flow\Scope('singleton')]
final class YourApi
{
    #[Query]
    public function ping(string $name): string {
        return strtoupper($name);
    }
}

现在在某个 Objects.yaml 配置中为 虚拟对象 定义 HTTP 中间件

// Objects.yaml
'Your.Package:GraphQLMiddleware':
  className: 'Wwwision\GraphQL\GraphQLMiddleware'
  scope: singleton
  factoryObjectName: Wwwision\GraphQL\GraphQLMiddlewareFactory
  arguments:
    1:
      # GraphQL URL
      value: '/graphql'
    2:
      # PHP Class with the Query/Mutation attributed methods
      value: 'Your\Package\YourApi'

最后,在 Settings.yaml 中注册该自定义中间件

// Settings.yaml
Neos:
  Flow:
    http:
      middlewares:
        'Your.Package:GraphQL':
          position: 'before routing'
          middleware: 'Your.Package:GraphQLMiddleware'

这样,一个可工作的 GraphQL API 就可以在 /graphql 下方访问。

复杂类型

默认情况下,与指定 API 类 相同命名空间 的所有类型都将自动解析,因此您可以这样做

// YourApi.php
// ...
#[Query]
public function ping(Name $name): Name {
    return strtoupper($name);
}

只要在同一命名空间(Your\Package)中存在合适的 Name 对象即可。要支持来自 不同 命名空间的类型,可以在 GraphQLMiddlewareFactory 的第三个参数中指定它们

// Objects.yaml
'Your.Package:GraphQLMiddleware':
  # ...
  arguments:
    # ...
    # Look for classes in the following namespaces when resolving types:
    3:
      value:
        - 'Your\Package\Types'
        - 'SomeOther\Package\Commands'

身份验证

通常,GraphQL 中间件是在路由中间件之前执行的。因此,Security\Context 尚未初始化。此包允许您通过模拟 MVC 请求来“模拟”初始化安全性。这是通过 GraphQLMiddlewareFactory 的第四个参数完成的

// Objects.yaml
'Your.Package:GraphQLMiddleware':
  # ...
  arguments:
    # ...
    # Simulate a request to the Neos NodeController in order to initialize the security context and trigger the default Neos backend authentication provider
    4:
      value: 'Neos\Neos\Controller\Frontend\NodeController'

重要

由于 Flow 解析此配置的方式,必须确保参数定义之间没有空格。要仅指定模拟控制器,可以将第三个参数传递为空 value: [] 数组

自定义解析器

从版本 5.2 开始,可以注册自定义函数,以动态扩展类型的操作行为

// Objects.yaml
'Your.Package:GraphQLMiddleware':
  # ...
  arguments:
    # ...
    # custom resolvers
    5:
      value:
        'User':
          'fullName':
            description: 'Custom resolver for User.fullName'
            resolverClassName: Some\Package\SomeCustomResolvers
            resolverMethodName: 'getFullName'
          'isAllowed':
            resolverClassName: Some\Package\SomeCustomResolvers

注意

如果自定义字段名与 resolverMethodName 相同,则可以省略它

重要

由于 Flow 解析此配置的方式,必须确保参数定义之间没有空格。要仅指定自定义解析,可以将第三个参数传递为空 value: [] 数组,并将第四个参数传递为 value: null

所有自定义解析器都必须是公共函数,以扩展类型作为第一个参数(以及可选的附加参数),并指定返回类型。

例如,上面的解析器类可能如下所示:

final class SomeCustomResolvers {

    public function __construct(private readonly SomeDependency $incjection) {}
    
    public function getFullName(User $user): string {
        return $user->givenName . ' ' . $user->familyName;
    }
    
    public function isAllowed(User $user, Privilege $privilege): bool {
        return $this->incjection->isUserPrivilegeAllowed($user->id, $privilege);
    }
}

更多

有关更多示例和如何使用更复杂类型的说明,请参阅wwwision/typeswwwision/types-graphql

常见问题解答(FAQ)

问题:如何实现延迟加载?

版本5.0的全面重写导致默认情况下加载和编码类型的所有字段。根据我的经验,这由于减少了与每个字段延迟加载相关的i/o和序列化/反序列化,从而提高了性能。然而,如果您处理高度复杂或嵌套的类型,预先加载所有字段的开销可能成为问题。在这种情况下,您可以通过添加更多特定的根级查询来简化结构。或者,您可以使用自定义解析器

我想让提供延迟加载字段更容易(参见bwaidelich/types-graphql#6),但当前我没有这个功能的个人需求。

问题:我遇到Neos Flow代理类的问题

构建在wwwision/types之上的此库依赖于构造函数包含所有涉及的字段(请参阅https://github.com/bwaidelich/types/blob/main/README.md#all-state-fields-in-the-constructor)。Flow代理类(以及由于错误实际上几乎每个Flow包的类)覆盖了没有参数的构造函数。

作为解决方案,您可以在受影响的类中添加#Flow\Proxy(false)属性。

我正在考虑向解析器添加一个扩展点,以允许开箱即用的代理类(请参阅bwaidelich/types#6),但当前我没有这个功能的个人需求。

问题:关于从 doctrine 实体生成 GraphQL 模式如何处理?

直接将实体暴露给API可能会引发问题,因为它增加了耦合,并可能阻碍维护。然而,有时,特别是对于较小的API,使用核心和“边缘”相同实体类和值对象是最实用的解决方案。我个人会避免暴露(甚至使用)doctrine 实体,因为它们往往会导致贫血领域模型并将核心领域与基础设施耦合。相反,我更愿意尽可能使用PHP类型系统(以及wwwision/types包进行验证)和适配器来映射这些实体和数据库记录。

有了这一切,只要构造函数包含所有字段并且类不是由Flow代理(请参阅上述内容),仍然可以从doctrine实体推导出GraphQL模式。

 use Doctrine\ORM\Mapping as ORM;
 use Neos\Flow\Annotations as Flow;

 /**
  * @ORM\Entity
  * @Flow\Proxy(false)
  */
class TestEntity
{

    /**
     * @var string
     * @ORM\Id
     */
    public readonly string $id;

    /**
     * @var string
     * @ORM\Column(length=80)
     */
    public readonly string $title;

    public function __construct(string $id, string $title) {
        $this->id = $id;
        $this->title = $title;
    }
}

通过bwaidelich/types#6,集成可能得到改进。

问题:如何处理5.0版本中的破坏性更改?

版本 5.0 是对该软件包的重大重写,拥有新的基础和哲学。如果您完全不能接受这种做法,您仍然可以使用该软件包的旧版本,我计划支持 4.x 版本一段时间!

问:关于功能 x 的内容?

我主要创建这个软件包是为了自己的项目以及客户的那些项目,但当然,看到它在其他地方被使用也让我感到很高兴。因此,请随时提供 功能建议 或甚至 实现方案,但请不要期望我会完全遵守,因为我必须在我业余时间维护这个软件包。

如果您需要实现特定的功能或修复错误,您当然也可以雇佣我来完成这些工作!

贡献

问题拉取请求 形式做出的贡献非常受欢迎

许可协议

请参阅 LICENSE