wwwision / graphql
使用 Flow 创建 GraphQL 端点的基本包
Requires
- php: >=8.1
- neos/flow: ^7.3 || ^8.0
- psr/http-message: ^1 || ^2
- webonyx/graphql-php: ^15
- wwwision/types-graphql: ^1.2
Requires (Dev)
- roave/security-advisories: dev-latest
This package is auto-updated.
Last update: 2024-09-12 18:49:07 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/types和wwwision/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