amazeelabs / graphql_directives
基于指令的Drupal GraphQL模式。
Requires
- webmozart/glob: ^4.7.0
README
GraphQL模式实现的指令方法。提供了一种名为'Directable'的模式插件,它可以加载GraphQL模式定义文件,并使用指令插件实现。
用法
创建一个GraphQL模式定义文件,并用指令注解它。
type Query { hello: String @value(string: "Hello world!") }
使用“Directable”模式插件配置GraphQL服务器,并将模式定义路径设置为创建的模式文件。
指令定义将自动添加到模式中。为了支持具有自动补全和语法检查的IDE,有一个drush
命令可以生成包含所有指令及其实现位置信息的模式文件。
drush graphql:directives >> directives.graphqls
链式调用
指令可以链式调用以组合可重用的数据生成器。它们从左到右组成,意味着左侧指令的输出将作为父值传递给其右侧邻居。
type Query { # This will emit "three". list: String! @value(json: "[\"one\", \"two\", \"three\"]") @seek(pos: 2) }
映射
@map
指令允许遍历其左侧邻居的输出,并将右侧的所有指令应用于每个项目。
type Query { # This will emit ["a", "b"]. map: [String!]! @value(json: "[{\"x\": \"a\"},{\"x\": \"b\"}]") @map @prop(key: "x") }
默认值
由于Drupal的数据结构无法保证完整性,GraphQL模式将在可能的情况下强制执行默认值。每当类型在非空位置(末尾没有!
)中使用时,如果值是null
,则尝试应用默认值。默认值由类型决定,例如Int
类型的默认值是0
,Boolean
类型的默认值是false
,String
类型的默认值是""
,列表类型(如[String!]!
)的默认值是[]
。
对于自定义类型、接口、联合或标量,可以使用@default
指令开始一个生成默认值的指令链。
scalar MyScalar @default @value(string: "bar") type Query { # This will emit `''`. string: String! @value # This will emit `0`. int: Int! @value # This will emit `[]`. list: [String!]! @value # This will emit `bar` manual: MyScalar! @value }
类型解析
指令还可以用于解析联合和接口的运行时类型。为此,将任何可以用于解析字段值的指令应用于接口或联合。指令链应解析为字符串值,该值将被视为类型ID。
union Letters @prop(key: "type") = A | B
然后,将解析出的类型ID与带有@type
指令注解的对象类型进行匹配,以检索实际类型。
type A @type(id: "a") { type: String! } type B @type(id: "b") { type: String! }
参数处理
指令可以使用ArgumentTrait
来应用动态参数。如果指令参数等于$
,则当前值将作为参数值传递。如果它的$
后面跟有任何字符,则这些字符将用作键从当前查询参数中检索值。
实现此行为的参数将被标记为(动态)。
type Query { static: Post @loadEntity(type: "node", id: "1") parent: Post @value(int: 1) @loadEntity(type: "node", id: "$") argument(id: String!): Post @loadEntity(type: "node", id: "$id") }
指令
@value
@value
指令允许您为字段定义一个静态值,作为原始值或JSON编码的字符串。如果没有任何参数,它将发出null
。
type Query { null: String @value hello: String! @value(string: "Hello world!") theAnswer: Int! @value(int: 42) pi: Float! @value(float: 3.14) true: Boolean! @value(float: true) object: MyType! @value(json: "{\"foo\":\"bar\"}") }
@seek
从列表或可迭代对象中提取值。参数pos
标记目标位置。
type Query { # This will emit "three". list: String! @value(json: "[\"one\", \"two\", \"three\"]") @seek(pos: 2) }
@prop
从对象或映射中提取属性。参数key
标记目标键。
type Query { # This will emit "bar". prop: String! @value(json: "{\"foo\": \"bar\"}") @prop(key: "foo") }
@map
遍历当前结果列表,并将以下指令应用于每个项目。
type Query { # This will emit ["a", "b"]. map: [String!]! @value(json: "[{\"x\": \"a\"},{\"x\": \"b\"}]") @map @prop(key: "x") }
@type
为对象类型注解一个特定的ID,该ID将用于接口和联合类型解析。
union Letters @prop(key: "type") = A | B type A @type(id: "a") { type: String! } type B @type(id: "b") { type: String! }
@arg
检索参数值并将其注入为当前值,该值将作为父值传递给后续指令。
type Query { post(path: String!): Page @arg(name: "path") @route(path: "$") @loadEntity }
@route
将路径(动态)解析为Drupal Url
对象。
type Query { post(path: String!): Page @route(path: "$path") @loadEntity }
@loadEntity
以各种方式加载Drupal实体。如果没有参数,它假定父值包含由@route
生成的Url
对象,并尝试从那里加载实体。
如果与id
或uuid
一起使用,可选的operation
参数可以定义特定的访问检查操作。
type Query { post(path: String!): Page @route(path: "$path") @loadEntity }
否则,它需要定义一个静态的type
参数以及一个id
(动态)或uuid
(动态)参数。
type Query { id(id: String!): Post @loadEntity(type: "node", id: "$id") uuid(uuid: String!): Post @loadEntity(type: "node", uuid: "$uuid") }
@resolveEntity[...]
检索实体的各种简单属性。以下指令被支持:
@resolveEntityId
@resolveEntityUuid
@resolveEntityType
@resolveEntityBundle
@resolveEntityLabel
@resolveEntityPath
@resolveEntityLanguage
type Query { post(id: String!): Post @loadEntity(type: "node", id: "$id") } type Post { title: String! @resolveEntityLabel }
@resolveEntityTranslation
检索实体的特定翻译,由lang
(动态)参数定义。
type Query { post(id: String!, lang: String!): Post @loadEntity(type: "node", id: "$id") @resolveEntityTranslation(lang: "$lang") }
@resolveEntityTranslations
检索实体的所有翻译。
type Query { post(id: String!): Post @loadEntity(type: "node", id: "$id") } type Post { translations: [Post!]! @resolveEntityTranslations }
@resolveProperty
通过其path
参数检索实体的属性。
type Post { body: String @resolveProperty(path: "body.value") }
@lang
切换当前执行语言,用于当前字段以下的所有子树。接受一个code
参数(动态),如果省略,则使用父值。如果后者是字符串,则按原样使用,如果是TranslatableInteface
的实例,则从那里推导语言。
type Query { post(id: String!): Post @loadEntity(type: "node", id: "$id") @lang }
@resolveMenuItems
检索菜单实体的所有项。接受一个可选的max_level
参数,用于限制最大菜单级别数。树被展平为一个列表,以避免嵌套片段的必要性。应使用@resolveMenuItemId
和@resolveMenuItemParentId
指令在消费者中重建树。菜单项列表还通过语言过滤,尊重当前执行上下文语言,因为它可以通过@lang
控制。
type Query { menu: Limited! @loadEntity(type: "menu", id: "main", operation: "view label") } type Menu { items: [MenuItem!]! @lang(code: "fr") @resolveMenuItems(max_level: 2) }
@resolveMenuItem[...]
各种菜单项属性。
@resolveMenuItemId
@resolveMenuItemParentId
@resolveMenuItemLabel
@resolveMenuItemUrl
@resolveEntityReference
& @resolveEntityReferenceRevisions
解析附加到给定field
的引用实体。将尝试检索与当前宿主实体匹配的翻译。
type Query { post(id: String!): Post @loadEntity(type: "node", id: "$id") @lang } type Post { title: String! @resolveEntityLabel related: [Post!]! @resolveEntityReference(field: "field_related") }
@drupalView
执行Drupal视图。
type Query { contentHub(locale: String!, args: String): ContentHubView! @lang(code: "$locale") @drupalView(id: "content_hub:default", args: "$args") } type ContentHubView { total: Int! rows: [Post!]! filters: ContentHubViewFilters! } type ContentHubViewFilters { tag: [ViewFilter!]! category: [ViewFilter!]! } type ViewFilter { value: String! label: String! }
id
参数
视图ID和显示由冒号分隔。例如:content_hub:default
。
args
参数
查询字符串格式中的过滤器、页码等。例如:page=1&pageSize=9&contextualFilters=40/12&tag[]=1&type=foo
保留键
page
:页码。从1开始。默认为1。pageSize
:每页的项目数。默认为10。contextualFilters
:上下文过滤器值,例如40/12/10
。
其余键被视为过滤器。
允许使用NPM query-string
包的格式,其中arrayFormat
设置为bracket
,例如tag[]=1&tag[]=2
的数组值。
返回结果
结果的结构如下
type Result = { total: number; items: [DrupalEntity]; filters: { [filterKey: string]: Array<{ value: string; label: string; }>; }; };
翻译
使用@lang
指令为视图设置语言。如果结果实体具有所选语言,它们将返回在该语言中。
如果必须按语言过滤结果,请使用视图中的Content: Translation language (= Interface text language selected for page)
过滤器。
扩展
要添加自定义指令,请创建一个模块,并在src/Plugin/GraphQL/Directive
目录中添加新插件。插件的"ID"将是调用它的schema的句柄,没有@
前缀。
<?php namespace Drupal\my_directives\Plugin\GraphQL\Directive; use Drupal\graphql_directives\DirectiveInterface; /** * @Directive( * id="echo", * description="Return the same string that you put in.", * arguments = { * "input" = "String!", * } * ) */ class EchoDirective extends PluginBase implements DirectiveInterface { /** * {@inheritdoc} */ public function buildResolver( ResolverBuilder $builder, array $arguments ) : ResolverInterface { return $builder->fromValue($arguments['input']); } }
自动加载
@amazeelabs/codegen-autoloader
提供了一个使用 drupal
模式添加新指令的便捷选项。生成的 JSON 文件与此模块的 Autoload 注册表
配置选项兼容。
模式扩展
该模块提供了一个 DirectableSchemaExtensionPluginBase
类,可用于创建响应父模式定义中指令的模式扩展。Drupal GraphQL 模块的模式扩展插件提供了两个模式定义:一个用于 "基本" 模式,另一个用于实际扩展。在可定向模式扩展的情况下,基本模式定义应包含指令,而扩展模式定义了派生类型和字段。
请参考 graphql_directives_test
模块以获取一个非常简单的示例。