Iteração e Manipulação de Valores de Campos
Adição de meta-diretivas ao schema GraphQL, para iterar e manipular os elementos do valor de campos do tipo array e objeto:
@underArrayItem@underJSONObjectProperty@underEachArrayItem@underEachJSONObjectProperty@objectClone
@underArrayItem
@underArrayItem faz com que a diretiva aninhada seja aplicada em um item específico do array.
Na query abaixo, apenas o primeiro item do array com os nomes das categorias é transformado para maiúsculas:
query {
posts {
categoryNames
@underArrayItem(index: 0)
@strUpperCase
}
}...produzindo:
{
"data": {
"posts": {
"categoryNames": [
"NEWS",
"sports"
]
}
}
}@underJSONObjectProperty
@underJSONObjectProperty faz com que a diretiva aninhada receba uma entrada do objeto JSON consultado.
Esta diretiva é particularmente útil para extrair e manipular um dado desejado após consultar uma API externa, que muito provavelmente terá um tipo genérico JSONObject (como ao usar o campo de função _sendJSONObjectItemHTTPRequest da extensão HTTP Client).
Na query abaixo, obtemos um objeto JSON proveniente da WP REST API, e usamos @underJSONObjectProperty para manipular a propriedade type da resposta, transformando-a em maiúsculas:
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
}Isso produzirá:
{
"data": {
"postData": {
"id": 1,
"date": "2019-08-02T07:53:57",
"type": "POST",
"title": {
"rendered": "Hello world!"
}
}
}
}Além de receber uma "key" para apontar para uma propriedade que está no primeiro nível do objeto JSON, esta diretiva também pode receber um "path" para navegar dentro da estrutura interna do objeto, usando . como separador entre os níveis.
Na query abaixo, o endpoint da WP REST API para um post fornece a propriedade "title.rendered". Podemos navegar até esse subelemento específico e transformá-lo para title case:
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
}Isso produzirá:
{
"data": {
"postData": {
"id": 1,
"date": "2019-08-02T07:53:57",
"type": "post",
"title": {
"rendered": "HELLO WORLD!"
}
}
}
}@underEachArrayItem
@underEachArrayItem itera sobre os itens do array de algum campo na entidade consultada, e executa a(s) diretiva(s) aninhada(s) em cada um deles.
Por exemplo, o campo Post.categoryNames é do tipo [String]. Usando @underEachArrayItem, podemos iterar os nomes das categorias e aplicar a diretiva @strTranslate a eles.
Nesta query, as categorias do post são traduzidas do inglês para o francês:
query {
posts {
id
title
categoryNames
@underEachArrayItem
@strTranslate(
from: "en",
to: "fr"
)
}
}...produzindo:
{
"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 pode passar tanto o índice quanto o valor do elemento iterado como variável dinâmica para sua(s) diretiva(s) aninhada(s), via args de diretiva passIndexOnwardsAs e passValueOnwardsAs.
Esta query demonstra o uso das variáveis dinâmicas $index e $value:
{
_echo(value: ["first", "second", "third"])
@underEachArrayItem(
passIndexOnwardsAs: "index"
passValueOnwardsAs: "value"
)
@applyField(
name: "_echo"
arguments: {
value: {
index: $index,
value: $value
}
},
setResultInResponse: true
)
}O resultado é:
{
"data": {
"_echo": [
{
"index": 0,
"value": "first"
},
{
"index": 1,
"value": "second"
},
{
"index": 2,
"value": "third"
}
]
}
}@underEachArrayItem também pode limitar as posições do array sobre as quais iterar, via param filter->by, que pode aceitar a entrada include ou exclude.
Esta 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
}...produz:
{
"data": {
"including": [
"FIRST",
"second",
"THIRD"
],
"excluding": [
"first",
"SECOND",
"third"
]
}
}@underEachJSONObjectProperty
@underEachJSONObjectProperty é similar a @underEachArrayItem, mas opera em elementos JSONObject.
Nesta query, iteramos todas as entradas do objeto JSON e substituímos qualquer entrada null por uma string vazia:
{
_echo(
value: {
first: "hello",
second: "world",
third: null
}
)
@underEachJSONObjectProperty
@default(value: "")
}...produzindo:
{
"data": {
"_echo": {
"first": "hello",
"second": "world",
"third": ""
}
}
}@underEachJSONObjectProperty pode passar a chave e o valor sobre os quais está iterando como variável dinâmica para sua(s) diretiva(s) aninhada(s), via args de diretiva passKeyOnwardsAs e passValueOnwardsAs.
Esta query demonstra o uso das variáveis dinâmicas $key e $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
)
}O resultado é:
{
"data": {
"_echo": {
"uno": {
"key": "uno",
"value": "first"
},
"dos": {
"key": "dos",
"value": "second"
},
"tres": {
"key": "tres",
"value": "third"
}
}
}
}@underEachJSONObjectProperty também pode limitar as chaves do objeto JSON sobre as quais iterar, via param filter->by, que pode aceitar a entrada includeKeys ou excludeKeys.
Esta 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
}...produz:
{
"data": {
"includingKeys": {
"uno": "FIRST",
"dos": "second",
"tres": "THIRD"
},
"excludingKeys": {
"uno": "first",
"dos": "SECOND",
"tres": "third"
}
}
}@objectClone
Objetos JSON podem ser acessados por referência nos resolvers de campos (e não por cópia/duplicação do objeto). Quando esse é o caso, ao modificar o objeto JSON, essa modificação será visível para todos os campos que recuperam esse objeto JSON.
Este é o caso do campo Block.attributes:
{
posts {
blocks(filterBy: { include: "core/heading" } ) {
attributes
}
}
}...que produz:
{
"data": {
"posts": [
{
"blocks": [
{
"attributes": {
"content": "Image Block (Full width)",
"level": 2
}
},
{
"attributes": {
"content": "Gallery Block",
"level": 2
}
}
]
}
]
}
}Na query abaixo, enquanto originalAttributes simplesmente recupera os atributos, transformedAttributes também traduzirá a propriedade content para o francês:
{
posts {
blocks(filterBy: { include: "core/heading" } ) {
originalAttributes: attributes
transformedAttributes: attributes
@underJSONObjectProperty(by: { key: "content" })
@strTranslate(to: "fr")
}
}
}Entretanto, como a entidade Block consultada referencia o mesmo objeto JSON tanto em originalAttributes quanto em transformedAttributes, as transformações realizadas pelo segundo campo também afetarão o primeiro (isso independe da ordem em que aparecem na query).
Como resultado, ambos os campos são traduzidos para o francês:
{
"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
}
}
]
}
]
}
}Podemos evitar esse problema adicionando a diretiva @objectClone no campo transformedAttributes, para que as modificações sejam realizadas em um objeto JSON clonado:
{
posts {
blocks(filterBy: { include: "core/heading" } ) {
originalAttributes: attributes
transformedAttributes: attributes
@objectClone
@underJSONObjectProperty(by: { key: "content" })
@strTranslate(to: "fr")
}
}
}...produzindo:
{
"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
}
}
]
}
]
}
}Exemplos adicionais
Nesta query, @underEachArrayItem envolve @underJSONObjectProperty, que por sua vez envolve @strUpperCase, transformando a propriedade "title.rendered" em maiúsculas, para as múltiplas entradas de posts obtidas via WP REST API:
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
}...produzindo:
{
"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"
}
}
]
}
}