rozbehsharahi / graphql3
TYPO3 的 GraphQL 扩展
Requires
- php: >=8.0
- ext-dom: *
- ext-pdo: *
- doctrine/inflector: ^2.0.6
- firebase/php-jwt: ^6.3.0
- symfony/security-core: ^v6.0.14
- typo3/cms-backend: ^11.5 || ^12
- typo3/cms-core: ^11.5 || ^12
- typo3/cms-extensionmanager: ^11.5 || ^12
- typo3/cms-frontend: ^11.5 || ^12
- webonyx/graphql-php: ^v15.0.1
Requires (Dev)
- ext-openssl: *
- ext-sodium: *
- phpstan/phpstan: 1.8.x-dev
- phpunit/phpunit: ^10
- roave/security-advisories: dev-latest
- rozbehsharahi/graphql3-test-extension: @dev
- typo3/cms-felogin: ^11.5 || ^12
- typo3/cms-fluid-styled-content: ^11.5 || ^12
- typo3/cms-install: ^11.5 || ^12
- typo3/cms-lowlevel: ^11.5 || ^12
- typo3/cms-rte-ckeditor: ^11.5 || ^12
- typo3/cms-tstemplate: ^11.5 || ^12
This package is auto-updated.
Last update: 2024-09-02 12:22:04 UTC
README
此扩展尚属新品,尚未经过真实用户的测试。您可以通过测试和使用 问题板 来贡献。
此包允许您为 TYPO3 页面注册一个 graphql 架构。
如果您注册了一个架构,它可以通过网站根页的 /graphql
或 /graphiql
访问
https://www.example.com/my-site-root/graphql
https://www.example.com/my-site-root/graphiql (仅开发环境使用)
使用方法
使用 webonyx/graphql-php
包的类型注册架构。
文档: https://webonyx.github.io/graphql-php/
graphql3
的用法如下所示
use GraphQL\Type\Schema; use RozbehSharahi\Graphql3\Registry\SchemaRegistry; /** @var SchemaRegistry $schemaRegistry */ $schemaRegistry->registerCreator(fn() => new Schema([ 'query' => new ObjectType([ 'name' => 'Query', 'fields' => [ 'noop' => [ 'type' => Type::string(), 'resolve' => fn () => 'noop', ], ], ]), ]))
之后,您应该已经能够访问您的 graphql 端点了。
register
方法期望一个 webonyx/graphql-php
包的架构,因此您可以从这里开始做任何事情。
然而,graphql3
的主要重点是提供可扩展的构建器/类型/节点/解析器,这将有助于在 TYPO3 网站上引入 GraphQL。
例如,以下代码完全等价,但使用了内部类型之一。
use RozbehSharahi\Graphql3\Registry\SchemaRegistry; use RozbehSharahi\Graphql3\Type\NoopQueryType; /** @var SchemaRegistry $schemaRegistry */ $schemaRegistry->registerCreator(fn () => (new NoopQueryType());
为了有一些真正的 TYPO3 代码,请继续下一章 入门
。
入门
我们假设您有一个运行的 TYPO3 扩展和一个 Configuration/Services.yaml
(如下),这将使构造函数注入和服务定位工作。
services: _defaults: autowire: true autoconfigure: true public: false Your\Extension\: resource: '../Classes/*' exclude: '../Classes/Domain/Model/*'
在扩展的 Classes
目录中创建一个 graphql 设置类。
<?php namespace Your\Extension; use GraphQL\Type\Schema; use RozbehSharahi\Graphql3\Registry\SchemaRegistry; use RozbehSharahi\Graphql3\Setup\SetupInterface; use RozbehSharahi\Graphql3\Type\QueryType; class GraphqlSetup implements SetupInterface { public function __construct( protected SchemaRegistry $schemaRegistry, protected QueryType $queryType ) { } public function setup(): void { $this->schemaRegistry->registerCreator(fn() => new Schema([ 'query' => $this->queryType, ])); } }
您可以将
GraphqlSetup
类放在任何地方。只要您实现了GraphqlSetupInterface
,graphql3 就会自动检测您的类并调用setup
方法。但是,请确保清除所有缓存!
此时,您的 graphql 端点应该已经可访问。
https://../your-site-root/graphql
https://../your-site-root/graphiql # in dev mode
内置的 QueryType
已经为以下实体提供了基于 tca 的架构
- 页面
- tt_content
- 语言
例如
{
pages {
items {
title
subtitle
parentPage {
title
}
createdAt(format: "Y-m-d h:i")
media {
uid
extension
size
publicUrl
imageUrl(variant: "default", maxHeight: 100)
}
}
}
contents {
items {
header
bodytext
rendered
}
}
}
通过实现 QueryTypeExtenderInterface
,扩展架构非常简单。
<?php declare(strict_types=1); namespace Your\Extension; use GraphQL\Type\Definition\Type; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNode; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNodeCollection; use RozbehSharahi\Graphql3\Type\QueryTypeExtenderInterface; class ExampleQueryTypeExtender implements QueryTypeExtenderInterface { public function extend(GraphqlNodeCollection $nodes): GraphqlNodeCollection { return $nodes->add( GraphqlNode::create('someNode') ->withType(Type::string()) ->withResolver(fn () => 'Hello World') ); } }
QueryType
实现使用了内置的基于 tca 的构建器。如果您想在 graphql 中添加自己的实体,也可以使用它们
<?php declare(strict_types=1); namespace Your\Extension; use RozbehSharahi\Graphql3\Builder\RecordListNodeBuilder; use RozbehSharahi\Graphql3\Builder\RecordNodeBuilder; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNodeCollection; use RozbehSharahi\Graphql3\Type\QueryTypeExtenderInterface; class ExampleQueryTypeExtender implements QueryTypeExtenderInterface { public function __construct( protected RecordNodeBuilder $recordNodeBuilder, protected RecordListNodeBuilder $recordListNodeBuilder ) { } public function extend(GraphqlNodeCollection $nodes): GraphqlNodeCollection { return $nodes ->add($this->recordListNodeBuilder->for('sys_log')->build()) ->add($this->recordNodeBuilder->for('sys_log')->build()) ; } }
在给定的示例中,使用了 record-node-builder 和 record-list-node-builder。这些类将根据 TCA 配置自动生成一个 graphql 架构。现在可以运行以下查询
{
sysLogs {
items {
updatedAt
details
type
}
}
}
在 graphql3 上注册架构将激活 graphql 端点。这项任务应该由单个扩展完成,在大多数情况下是您的项目主要扩展。其他扩展应仅提供架构字段和类型...,这将在下一章 文档
中解释。
文档
让我们从 QueryType
开始。
QueryType
查询类型是基本的查询配置,您可以使用它开始。它已经提供了一些根节点,如 page
。然而,可以在运行时扩展查询,这使任何扩展都可以钩入。
<?php namespace Your\Extension; use GraphQL\Type\Schema; use RozbehSharahi\Graphql3\Registry\SchemaRegistry; use RozbehSharahi\Graphql3\Setup\SetupInterface; use RozbehSharahi\Graphql3\Type\QueryType; class GraphqlSetup implements SetupInterface { public function __construct( protected SchemaRegistry $schemaRegistry, protected QueryType $queryType ) { } public function setup(): void { $this->schemaRegistry->registerCreator(fn() => new Schema([ 'query' => $this->queryType, ])); } }
您可能已经检查了您的 /graphiql
路由,并发送了以下查询
{
page(uid: 1) {
uid
title
parentPage { title }
}
}
QueryType
通过 QueryTypeExtenderInterface
暴露根节点/字段的配置。通过实现该接口,您可以编辑根查询上的所有节点。
记录类型构建器
为了暴露TYPO3表格,需要一个GraphQL记录类型。GraphQL3提供了一个基于TCA的记录类型构建器。该构建器将根据TCA配置生成webonyx/graphql
对象类型。
以下示例展示了使用record-type-builder创建根查询中的页面节点。
<?php namespace Your\Extension; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Schema; use RozbehSharahi\Graphql3\Builder\RecordTypeBuilder; use RozbehSharahi\Graphql3\Domain\Model\Record; use RozbehSharahi\Graphql3\Registry\SchemaRegistry; use RozbehSharahi\Graphql3\Setup\SetupInterface; class GraphqlSetup implements SetupInterface { public function __construct( protected SchemaRegistry $schemaRegistry, protected RecordTypeBuilder $recordTypeBuilder ) { } public function setup(): void { $this->schemaRegistry->registerCreator(fn() => new Schema([ 'query' => new ObjectType([ 'name' => 'Query', 'fields' => [ 'page' => [ 'type' => $this->recordTypeBuilder->for('pages')->build(), 'resolve' => fn () => Record::create('pages', [ 'uid' => 1, 'title' => 'A hard coded page, which should be loaded by a resolver' ]), ], ], ]), ])); } }
在给定的示例中,只将一个硬编码的页面数组传递给类型。
拥有一个
uid
参数和通过该uid
加载页面的解析器是非常有意义的。这将在后续章节中解决。
除了动态创建记录类型之外,构建器还将提供可扩展性和类型缓存。
任何扩展都可以通过实现\RozbehSharahi\Graphql3\Builder\RecordTypeBuilderExtenderInterface
来钩入任何表格的类型创建。
以下代码展示了如何通过添加额外的节点md5
来扩展页面类型。
<?php declare(strict_types=1); namespace Your\Extension; use RozbehSharahi\Graphql3\Builder\RecordTypeBuilderExtenderInterface; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNode; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNodeCollection; use RozbehSharahi\Graphql3\Domain\Model\Tca\TableConfiguration; class Md5PageTypeExtender implements RecordTypeBuilderExtenderInterface { public function supportsTable(TableConfiguration $tableConfiguration): bool { return 'pages' === $tableConfiguration->getName(); } public function extendNodes( TableConfiguration $tableConfiguration, GraphqlNodeCollection $nodes ): GraphqlNodeCollection { return $nodes->add( GraphqlNode::create('md5')->withResolver(fn ($page) => md5(json_encode($page, JSON_THROW_ON_ERROR))) ); } }
只要类实现了
\RozbehSharahi\Graphql3\Builder\RecordTypeBuilderExtenderInterface
,位置并不重要。Symfony依赖注入将负责加载扩展器。但是,请清除缓存!
扩展器也可以删除或编辑现有字段。为此,请查看GraphqlNodeCollection
和GraphqlNode
,它们将在《GraphqlNode和GraphqlNodeCollection》章节中解释。
节点构建器
GraphQL3提供了节点构建器,以方便创建整个节点。这包括类型、解析器和参数。
每个节点构建器都实现了NodeBuilderInterface
,这意味着它提供了一个返回GraphqlNode
实例的构建方法。
在接下来的章节中,我们将以record-type-builder为例。查看vendor/rozbehsharahi/graphql3/Classes/Builder
以了解其他存在并可使用的构建器。
创建一个有意义的页面节点时,我们很可能会需要
- 一个加载页面的解析器
- 一个uid参数,它定义了要加载哪个页面
- 一个描述页面字段的类型
可以使用以下方式使用RecordNodeBuilder
<?php declare(strict_types=1); namespace Your\Extension; use GraphQL\Type\Definition\ObjectType; use GraphQL\Type\Schema; use RozbehSharahi\Graphql3\Builder\RecordNodeBuilder; use RozbehSharahi\Graphql3\Registry\SchemaRegistry; use RozbehSharahi\Graphql3\Setup\SetupInterface; class GraphqlSetup implements SetupInterface { public function __construct( protected SchemaRegistry $schemaRegistry, protected RecordNodeBuilder $recordNodeBuilder ) { } public function setup(): void { $this->schemaRegistry->registerCreator(fn() => new Schema([ 'query' => new ObjectType([ 'name' => 'Query', 'fields' => [ 'page' => $this->recordNodeBuilder->for('pages')->build()->toArray() ], ]), ])); } }
底层操作如下
- 创建一个
uid
参数 - 通过
\RozbehSharahi\Graphql3\Resolver\RecordResolver
创建解析器 - 通过
\RozbehSharahi\Graphql3\Builder\RecordTypeBuilder
创建页面类型
这将激活许多功能。例如
- 访问检查
- 通过扩展器扩展
- 灵活地将TCA字段映射到GraphQL字段
列表节点构建器
记录节点构建器将创建单个节点,如page
、content
、logs
,列表节点构建器将创建pages
、content
、logs
。
它们自带一系列内置功能,如
- 分页
- 过滤
- 计数
- 分面(将随后介绍)
假设您已将sys_log
添加到您的GraphQL端点中,如下所示
<?php declare(strict_types=1); namespace Your\Extension; use RozbehSharahi\Graphql3\Builder\RecordListNodeBuilder; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNodeCollection; use RozbehSharahi\Graphql3\Type\QueryTypeExtenderInterface; class ExampleQueryTypeExtender implements QueryTypeExtenderInterface { public function __construct( protected RecordListNodeBuilder $recordListNodeBuilder ) { } public function extend(GraphqlNodeCollection $nodes): GraphqlNodeCollection { return $nodes ->add($this->recordListNodeBuilder->for('sys_log')->build()) ; } }
通过这种方式,您已经可以进行以下查询
sysLogs(
pageSize:2
page: 2
filters: [
{type: "eq", field: "type", value: "2"}
]
) {
items {
updatedAt
details
type
}
}
将提供内置过滤器的更完整文档。
语言
列表节点构建器在sys_language_uid
关系方面遵循以下逻辑
当表格在TCA上设置了languageField
时,它将为语言过滤添加一个额外的查询参数。
{
pages(language: "de") {
...
}
}
该参数目前期望的是twoLetterIsoCode
。如果给定语言在当前站点上可用,它将被设置为查询的过滤器。不可用的语言将导致异常,并显示描述性错误消息。
语言被继承到子关系中。例如,获取语言为"en"的页面记录将导致子记录也被过滤为语言"en"。
访问控制
graphql3
的访问控制是基于 symfony/security-core
包实现的。它作为基于 JWT 令牌的安全应用程序实现。
JWT 认证
graphql3
上的所有访问控制相关代码都基于 jwt-authentication,这在 \RozbehSharahi\Graphql3\Domain\Model\JwtUser
类中进行了抽象。从核心代码的角度来看,只有通过 JWT 认证头(Bearer 令牌,header-line: Authorization)进行认证。因此,核心代码将不知道令牌的来源,但需要在 JWT 令牌上设置两个字段:字符串类型的 username
(用户名)和字符串数组类型的 roles
(角色)。
为了创建访问令牌,可以使用以下命令
# Manual creation of a token (asks for username and roles) vendor/bin/typo3 graphql3:create-token:manual # Creation of a token by frontend-user in database vendor/bin/typo3 graphql3:create-token:frontend-user [user-uid]
所有代码都将始终像 jwt-token 认证已发生一样操作,但是如果 graphql3
发现当前登录的 fe-user,它将将该用户映射到 jwt-token。这反映在实现的 \RozbehSharahi\Graphql3\Domain\Model\JwtUser::createFromTypo3Session
中。
为了在 JwtUser::$roles
(字符串数组)和 TYPO3 之间提供兼容性,实现了一种映射约定。当 jwt-user 需要与 TYPO3-Backend 用户组匹配时,它必须包含一个具有以下方案的用户角色:ROLE_GROUP_ID::[fe_group.uid]
。根据此约定,内部投票者将决定用户是否有权访问受特定 typo3 用户组限制的特定记录。为了抽象此约定,有一个方法 JwtUser::hasGroupId(x)
,它将底层调用 JwtUser::hasRole('ROLE_GROUP_ID::x')
。
将 TYPO3 用户映射到 JwtUser 的当前映射非常基础。大多数情况都应满足这一点。尽管如此,如果您需要例如用户组继承,则需要扩展 graphql3
。如果您需要此类功能,请在 Github 上的 Issue-Entry 中告诉我。
Jwt 认证设置/配置
目前 graphql3
支持以下算法
- RS256
- 带有密码保护的秘密的 RS256
- HS256
- EdDSA
您需要设置以下环境变量。如果没有定义公钥,则对于非对称签名(如 HS256)将回退到私钥。
- 私钥(用于创建令牌,例如
vendor/bin/typo3 graphql3:create-token:manual
) - 公钥(在像 RS256 这样的算法中需要用于验证令牌)
- 算法(例如 RS256,HS256,EdDSA,默认 RS256)
- 密码短语(如果私钥被密码加密则需要)
以下示例配置应该是最常见的设置
GRAPHQL3_JWT_ALGORITHM: "RS256" GRAPHQL3_JWT_PRIVATE_KEY: "file://my-path-to/private.pem" GRAPHQL3_JWT_PUBLIC_KEY: "file://my-path-to/public.pem" GRAPHQL3_JWT_PASSPHRASE: "" # Can stay empty if private key is not secured
在正确设置的环境下,您可以使用命令生成令牌。
请确保您的 apache 配置或 nginx 配置允许授权头。
// on apache .htaccess this line might be needed.
CGIPassAuth On
投票 & ACL
在底层,RecordNodeBuilder
使用一个内部的 RecordResolve
,该组件负责根据给定的 uid/slug 解析记录。但是,解析器做的不止这些。例如,它提供访问控制,这可以通过项目级别的 Voters
进行控制。
每次解析记录时,它都会传递给 AccessDecisionManager
。在 RecordListNodeBuilder
中也是如此,其中每个加载的记录都会进行检查是否有权限。
为了修改/添加您项目中的访问控制,您可以简单地创建一个实现 \RozbehSharahi\Graphql3\Voter\VoterInterface
的类。在实现接口时,您的投票者将自动添加到投票者堆栈中,无论您将其放置在哪里。
请确保实现
Graphql3
的VoterInterface
变体,而不是Symfony
的VoterInterface
。
<?php namespace Your\Extension; use RozbehSharahi\Graphql3\Domain\Model\JwtUser; use RozbehSharahi\Graphql3\Domain\Model\Record; use RozbehSharahi\Graphql3\Voter\VoterInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; class PageVoter implements VoterInterface { public function vote(TokenInterface $token, mixed $subject, array $attributes): int { if (!$subject instanceof Record || $subject->getTable()->getName() !== 'pages') { return self::ACCESS_ABSTAIN; } return $token->getUser() instanceof JwtUser ? self::ACCESS_GRANTED : self::ACCESS_DENIED; } }
如这里所示,投票者当然不是必需的,因为这已经由一个通用的内部 RecordResolver
处理。
访问决策管理器配置为使用 UnanimousStrategy
。这意味着所有投票者都必须授予或放弃访问权限。如果所有投票者都放弃,则授予访问权限。有关更多信息,请参阅 symfony/security
文档。
可以通过symfony的依赖注入容器在任何扩展的Configuration/Services.yaml
中修改现有的\RozbehSharahi\Graphql3\Voter\RecordVoter
。
灵活表单
简介
尽管GraphQL是一种强类型查询语言,但灵活表单在数据库方面更为宽松。灵活表单的数据是有类型的,然而,如果你深入研究TYPO3逻辑,会发现事情可能变得非常模糊。
可以在flexform xml中将字段类型化为整数,例如将settings.page
作为整数。然而,由于TYPO3具有根据另一张表中的字段(例如list_type
或更复杂的list_type, CType
)动态定义同一数据库字段的灵活表单特性,因此无法验证字段settings.page
始终为类型x。虽然可以将每个flex-form定义自动分散到其自己的作用域中,但这会在创建模式时造成大量的XML处理,并且会给代码带来很多复杂性。很可能实现这种自动模式创建的努力超过了它的实用性。
基于这些思考,graphql3
将灵活表单字段的集成委托给扩展的使用者,而不是神奇地构建庞大的模式。然而,它试图简化该过程。
在GraphQL中激活灵活表单字段
通过配置激活灵活表单字段目前处于实验状态。因此,API可能会频繁更改。请注意,您也可以简单地编写自己的record-type-extender来公开您的灵活表单字段!
灵活表单可以通过简单的方式访问
{
content(uid: 1) {
piFlexform
}
}
这将返回一个无类型安全的数组,它基本上是flex-form xml的JSON表示。
{ "data": { "content": { "piFlexform": { "settings": { "myField": "..." } } } } }
然而,graphql3为您提供了灵活表单字段的安全集成。为了将flex表单字段公开给GraphQL,您必须配置您的表TCA,以告诉graphql3
您的flex表单配置的路径。
$GLOBALS['TCA']['tt_content']['graphql3']['flexFormColumns'] = [ 'pi_flexform::default::settings.myField' ];
到达灵活表单字段的路径分为3部分
- 基本数据库列:
pi_flexform
- 数据结构变体
default
(参见TCA文档中的config.ds) - 灵活表单字段:
settings.myField
通过这种方式,graphql3
将根据settings.myField
的flex表单定义在内部创建一个“虚拟-tca-column”,并将其作为普通列传递给字段创建者堆栈。将为该字段创建一个唯一名称:flex_piFlexform_default_settings_myField
。您可以像为普通DB列那样配置自己的名称。
<settings.myField> <config> ... <graphql3> <name>fieldName</name> </graphql3> ... </config> </settings.myField>
如果您查看RozbehSharahi\Graphql3\Domain\Model\Record::get
的实现,您会注意到以flex::
开头的列名称将导致flex表单值查找。
这个概念使所有字段创建者都可以与flex表单列兼容。因此,在最佳情况下,每当您编写一个字段创建者时,它也将能够充当flex表单字段创建者。
请记住:在出现错误的情况下,您始终可以创建自己的record-type-extender来公开您的灵活表单字段,并且您可以通过问题板给我发消息。
突变
为了演示目的,graphql3
提供了一个用于创建sys_news
条目的突变。然而,突变的具体实现非常项目特定和上下文相关,graphql3
不会对此做出任何假设。现有的示例突变createSysNews
只有在具有ROLE_CREATE::sys_news
角色的令牌时才可用。
可以通过MutationTypeExtenderInterface
添加突变,当然您可以通过注入AccessChecker
来实现访问控制。
以下代码显示了突变的实现
<?php declare(strict_types=1); namespace Your\Extension; use GraphQL\Type\Definition\InputObjectType; use GraphQL\Type\Definition\Type; use RozbehSharahi\Graphql3\Domain\Model\GraphqlArgument; use RozbehSharahi\Graphql3\Domain\Model\GraphqlArgumentCollection; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNode; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNodeCollection; use RozbehSharahi\Graphql3\Security\AccessChecker; use RozbehSharahi\Graphql3\Type\MutationTypeExtenderInterface; use TYPO3\CMS\Core\Core\Environment; use TYPO3\CMS\Core\Database\ConnectionPool; class CreateSysNewsMutationTypeExtender implements MutationTypeExtenderInterface { public function __construct(protected ConnectionPool $connectionPool, protected AccessChecker $accessChecker) { } public function extend(GraphqlNodeCollection $nodes): GraphqlNodeCollection { if (!$this->accessChecker->check(['ROLE_CREATE::sys_news'])) { return $nodes; } return $nodes->add( GraphqlNode::create('createSysNews') ->withType(Type::int()) ->withArguments( GraphqlArgumentCollection::create()->add( GraphqlArgument::create('item')->withType(Type::nonNull( new InputObjectType([ 'name' => 'SysNewsInput', 'fields' => fn () => GraphqlNodeCollection::create() ->add(GraphqlNode::create('title')->withType(Type::string())) ->add(GraphqlNode::create('content')->withType(Type::string())) ->toArray(), ]) )) ) ) ->withResolver(function ($rootValue, $args) { $query = $this->connectionPool->getQueryBuilderForTable('sys_news'); $query->insert('sys_news')->values([ 'title' => $args['item']['title'], 'content' => $args['item']['content'], ]); $query->executeStatement(); return $query->getConnection()->lastInsertId('sys_news'); }) ); } }
实际上,突变和查询之间没有区别,您也可以在webonyx/graphql
文档中找到相关信息,因此您可以使用与查询相同的类和方法进行突变。
GraphqlNode 和 GraphqlNodeCollection
当扩展内置节点/类型/...时,您将在扩展器中接收到相关的模式部分,您可以自由地更改它们。例如,实现QueryTypeExtenderInterface
的查询类型扩展器将接收到根查询节点,然后可以进行编辑。然而,这不会像您预期的那样是一个数组。它将是一个GraphqlNodeCollection
。
本章将介绍GraphqlNode
和GraphqlNodeCollection
的工作原理。
GraphqlNode
简单地说是一个表示webonyx/graphql-php
数组的对象。它用于自动完成,可以简单地转换为数组。
<?php namespace Your\Namespace; use GraphQL\Type\Definition\Type;use RozbehSharahi\Graphql3\Domain\Model\GraphqlNode; $graphqlNode = GraphqlNode::create() ->withType(Type::string()) ->withResolver(fn() => 'hey') ->toArray(); // is equivalent to $graphqlNode = [ 'type' => Type::string(), 'resolve' => fn() => 'hey' ];
另一方面,您还有GraphqlNodeCollection
,它是GraphqlNode
对象的一个集合。
<?php namespace Your\Namespace; use GraphQL\Type\Definition\Type; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNode; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNodeCollection; $nodes = (new GraphqlNodeCollection()) ->add(GraphqlNode::create('myNode')->withType(Type::int())) ->add(GraphqlNode::create('myOtherNode')->withType(Type::string())) ->toArray(); // is equivalent to $nodes = [ 'myNode' => [ 'type' => Type::int() ], 'myOtherNode' => [ 'type' => Type::string() ] ];
请注意,graphql-nodes和-collections是不可变的。因此,在调用如add
或remove
等方法时,您需要使用返回值。
<?php namespace Your\Namespace; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNodeCollection; use RozbehSharahi\Graphql3\Domain\Model\GraphqlNode; $nodes = GraphqlNodeCollection::create(); $nodes = $nodes->add(GraphqlNode::create('aNewNode'));
GraphqlArgument
和GraphqlArgumentCollection
的实现几乎相同。通过自动完成查看您可以选择哪些选项。
贡献
如果您想积极支持此项目或添加功能,请查看贡献指南。