字段值的迭代与操作
向 GraphQL 模式添加元指令,用于迭代和操作数组及对象字段的值元素:
@underArrayItem@underJSONObjectProperty@underEachArrayItem@underEachJSONObjectProperty@objectClone
@underArrayItem
@underArrayItem 使嵌套指令作用于数组中的特定元素。
在下面的 query 中,只有分类名称数组中的第一个元素被转换为大写:
query {
posts {
categoryNames
@underArrayItem(index: 0)
@strUpperCase
}
}...结果:
{
"data": {
"posts": {
"categoryNames": [
"NEWS",
"sports"
]
}
}
}@underJSONObjectProperty
@underJSONObjectProperty 使嵌套指令能够接收被查询 JSON 对象中的某个条目。
该指令在查询外部 API 后提取并操作所需数据时特别有用,因为外部 API 几乎肯定会有一个泛型 JSONObject 类型(例如使用 HTTP Client 扩展的函数字段 _sendJSONObjectItemHTTPRequest 时)。
在下面的 query 中,我们获取来自 WP REST API 的 JSON 对象,并使用 @underJSONObjectProperty 操作响应的 type 属性,将其转换为大写:
query {
postData: _sendJSONObjectItemHTTPRequest(input: {
url: "https://newapi.getpop.org/wp-json/wp/v2/posts/1/?_fields=id,type,title,date"
})
@underJSONObjectProperty(by: { key: "type" })
@strUpperCase
}这将产生:
{
"data": {
"postData": {
"id": 1,
"date": "2019-08-02T07:53:57",
"type": "POST",
"title": {
"rendered": "Hello world!"
}
}
}
}除了接收 "key" 来指向 JSON 对象第一层的属性外,该指令还可以接收 "path" 来导航对象的内部结构,使用 . 作为层级分隔符。
在下面的 query 中,WP REST API 的文章端点提供了属性 "title.rendered"。我们可以导航到该实际子元素,并将其转换为标题大小写:
query {
postData: _sendJSONObjectItemHTTPRequest(input: {
url: "https://newapi.getpop.org/wp-json/wp/v2/posts/1/?_fields=id,type,title,date"
})
@underJSONObjectProperty(by: { path: "title.rendered" })
@strTitleCase
}这将产生:
{
"data": {
"postData": {
"id": 1,
"date": "2019-08-02T07:53:57",
"type": "post",
"title": {
"rendered": "HELLO WORLD!"
}
}
}
}@underEachArrayItem
@underEachArrayItem 遍历被查询实体某个字段的数组元素,并对每个元素执行嵌套指令。
例如,字段 Post.categoryNames 的类型为 [String]。使用 @underEachArrayItem,可以遍历分类名称并对其应用 @strTranslate 指令。
在此 query 中,文章的分类从英语翻译为法语:
query {
posts {
id
title
categoryNames
@underEachArrayItem
@strTranslate(
from: "en",
to: "fr"
)
}
}...结果:
{
"data": {
"posts": [
{
"id": 662,
"title": "Explaining the privacy policy",
"categoryNames": [
"Non classé"
]
},
{
"id": 28,
"title": "HTTP caching improves performance",
"categoryNames": [
"Avancé"
]
},
{
"id": 25,
"title": "Public or Private API mode, for extra security",
"categoryNames": [
"Ressource",
"Blog",
"Avancé"
]
}
]
}
}@underEachArrayItem 可以通过指令参数 passIndexOnwardsAs 和 passValueOnwardsAs,将迭代元素的索引和值作为动态变量传递给嵌套指令。
此 query 演示了动态变量 $index 和 $value 的用法:
{
_echo(value: ["first", "second", "third"])
@underEachArrayItem(
passIndexOnwardsAs: "index"
passValueOnwardsAs: "value"
)
@applyField(
name: "_echo"
arguments: {
value: {
index: $index,
value: $value
}
},
setResultInResponse: true
)
}结果为:
{
"data": {
"_echo": [
{
"index": 0,
"value": "first"
},
{
"index": 1,
"value": "second"
},
{
"index": 2,
"value": "third"
}
]
}
}@underEachArrayItem 还可以通过参数 filter->by 限制要迭代的数组位置,该参数可接受 include 或 exclude 条目。
此 query:
{
including: _echo([
"first",
"second",
"third"
])
@underEachArrayItem(
filter: {
by: {
include: [0, 2]
}
}
)
@strUpperCase
excluding: _echo([
"first",
"second",
"third"
])
@underEachArrayItem(
filter: {
by: {
exclude: [0, 2]
}
}
)
@strUpperCase
}...结果:
{
"data": {
"including": [
"FIRST",
"second",
"THIRD"
],
"excluding": [
"first",
"SECOND",
"third"
]
}
}@underEachJSONObjectProperty
@underEachJSONObjectProperty 与 @underEachArrayItem 类似,但作用于 JSONObject 元素。
在此 query 中,我们遍历 JSON 对象中的所有条目,并将任何 null 条目替换为空字符串:
{
_echo(
value: {
first: "hello",
second: "world",
third: null
}
)
@underEachJSONObjectProperty
@default(value: "")
}...结果:
{
"data": {
"_echo": {
"first": "hello",
"second": "world",
"third": ""
}
}
}@underEachJSONObjectProperty 可以通过指令参数 passKeyOnwardsAs 和 passValueOnwardsAs,将迭代的键和值作为动态变量传递给嵌套指令。
此 query 演示了动态变量 $key 和 $value 的用法:
{
_echo(value: {
uno: "first",
dos: "second",
tres: "third"
})
@underEachJSONObjectProperty(
passKeyOnwardsAs: "key"
passValueOnwardsAs: "value"
)
@applyField(
name: "_echo"
arguments: {
value: {
key: $key,
value: $value
}
},
setResultInResponse: true
)
}结果为:
{
"data": {
"_echo": {
"uno": {
"key": "uno",
"value": "first"
},
"dos": {
"key": "dos",
"value": "second"
},
"tres": {
"key": "tres",
"value": "third"
}
}
}
}@underEachJSONObjectProperty 还可以通过参数 filter->by 限制要迭代的 JSON 对象键,该参数可接受 includeKeys 或 excludeKeys 条目。
此 query:
{
includingKeys: _echo(value: {
uno: "first",
dos: "second",
tres: "third"
})
@underEachJSONObjectProperty(
filter: {
by: {
includeKeys: ["uno", "tres"]
}
}
)
@strUpperCase
excludingKeys: _echo(value: {
uno: "first",
dos: "second",
tres: "third"
})
@underEachJSONObjectProperty(
filter: {
by: {
excludeKeys: ["uno", "tres"]
}
}
)
@strUpperCase
}...结果:
{
"data": {
"includingKeys": {
"uno": "FIRST",
"dos": "second",
"tres": "THIRD"
},
"excludingKeys": {
"uno": "first",
"dos": "SECOND",
"tres": "third"
}
}
}@objectClone
JSON 对象在字段解析器中可能以引用方式访问(而非复制/复制对象)。当这种情况发生时,JSON 对象被修改后,修改将对所有获取该 JSON 对象的字段可见。
字段 Block.attributes 就属于这种情况:
{
posts {
blocks(filterBy: { include: "core/heading" } ) {
attributes
}
}
}...结果:
{
"data": {
"posts": [
{
"blocks": [
{
"attributes": {
"content": "Image Block (Full width)",
"level": 2
}
},
{
"attributes": {
"content": "Gallery Block",
"level": 2
}
}
]
}
]
}
}在下面的 query 中,originalAttributes 仅获取属性,而 transformedAttributes 还将 content 属性翻译为法语:
{
posts {
blocks(filterBy: { include: "core/heading" } ) {
originalAttributes: attributes
transformedAttributes: attributes
@underJSONObjectProperty(by: { key: "content" })
@strTranslate(to: "fr")
}
}
}然而,由于被查询的 Block 实体在 originalAttributes 和 transformedAttributes 上引用的是同一个 JSON 对象,后者字段执行的转换也会影响前者字段(这与它们在 query 中出现的顺序无关)。
因此,两个字段都被翻译为法语:
{
"data": {
"posts": [
{
"blocks": [
{
"originalAttributes": {
"content": "Bloc d'image (pleine largeur)",
"level": 2
},
"transformedAttributes": {
"content": "Bloc d'image (pleine largeur)",
"level": 2
}
},
{
"originalAttributes": {
"content": "Bloc Galerie",
"level": 2
},
"transformedAttributes": {
"content": "Bloc Galerie",
"level": 2
}
}
]
}
]
}
}通过在 transformedAttributes 字段上添加指令 @objectClone,可以避免此问题,使修改作用于克隆的 JSON 对象:
{
posts {
blocks(filterBy: { include: "core/heading" } ) {
originalAttributes: attributes
transformedAttributes: attributes
@objectClone
@underJSONObjectProperty(by: { key: "content" })
@strTranslate(to: "fr")
}
}
}...结果:
{
"data": {
"posts": [
{
"blocks": [
{
"originalAttributes": {
"content": "Image Block (Full width)",
"level": 2
},
"transformedAttributes": {
"content": "Bloc d'image (pleine largeur)",
"level": 2
}
},
{
"originalAttributes": {
"content": "Gallery Block",
"level": 2
},
"transformedAttributes": {
"content": "Bloc Galerie",
"level": 2
}
}
]
}
]
}
}更多示例
在此 query 中,@underEachArrayItem 包裹 @underJSONObjectProperty,@underJSONObjectProperty 再包裹 @strUpperCase,将通过 WP REST API 获取的多个文章条目的 "title.rendered" 属性转换为大写:
query {
postListData: _sendJSONObjectCollectionHTTPRequest(
url: "https://newapi.getpop.org/wp-json/wp/v2/posts/?per_page=3&_fields=id,type,title,date"
)
@underEachArrayItem
@underJSONObjectProperty(by: { path: "title.rendered" })
@strUpperCase
}...结果:
{
"data": {
"postListData": [
{
"id": 1692,
"date": "2022-04-26T10:10:08",
"type": "post",
"title": {
"rendered": "MY BLOGROLL"
}
},
{
"id": 1657,
"date": "2020-12-21T08:24:18",
"type": "post",
"title": {
"rendered": "A TALE OF TWO CITIES – TEASER"
}
},
{
"id": 1499,
"date": "2019-08-08T02:49:36",
"type": "post",
"title": {
"rendered": "COPE WITH WORDPRESS: POST DEMO CONTAINING PLENTY OF BLOCKS"
}
}
]
}
}