Query-functies
Query-functiesIteratie en Manipulatie van Veldwaarden

Iteratie en Manipulatie van Veldwaarden

Included in the “Power Extensions” bundle

Toevoeging van meta-directives aan het GraphQL-schema, voor het itereren en manipuleren van de waarde-elementen van array- en objectvelden:

  1. @underArrayItem
  2. @underJSONObjectProperty
  3. @underEachArrayItem
  4. @underEachJSONObjectProperty
  5. @objectClone

@underArrayItem

@underArrayItem zorgt ervoor dat de geneste directive wordt toegepast op een specifiek item uit de array.

In de onderstaande query wordt alleen het eerste item in de array met categorienamen omgezet naar hoofdletters:

query {
  posts {
    categoryNames
      @underArrayItem(index: 0)
        @strUpperCase
  }
}

...wat het volgende oplevert:

{
  "data": {
    "posts": {
      "categoryNames": [
        "NEWS",
        "sports"
      ]
    }
  }
}

@underJSONObjectProperty

@underJSONObjectProperty zorgt ervoor dat de geneste directive een item ontvangt uit het opgevraagde JSON-object.

Deze directive is bijzonder nuttig om een gewenst stuk data te extraheren en te manipuleren na het opvragen van een externe API, die hoogstwaarschijnlijk een generiek JSONObject-type heeft (zoals bij het gebruik van het functieveld _sendJSONObjectItemHTTPRequest uit de HTTP Client-extensie).

In de onderstaande query halen we een JSON-object op uit de WP REST API en gebruiken we @underJSONObjectProperty om de type-eigenschap van de respons te manipuleren en naar hoofdletters te transformeren:

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
}

Dit levert het volgende op:

{
  "data": {
    "postData": {
      "id": 1,
      "date": "2019-08-02T07:53:57",
      "type": "POST",
      "title": {
        "rendered": "Hello world!"
      }
    }
  }
}

Naast het ontvangen van een "key" om naar een eigenschap op het eerste niveau van het JSON-object te verwijzen, kan deze directive ook een "path" ontvangen om binnen de interne structuur van het object te navigeren, waarbij . als scheidingsteken tussen niveaus wordt gebruikt.

In de onderstaande query levert het WP REST API-endpoint voor een bericht de eigenschap "title.rendered". We kunnen naar dat specifieke subelement navigeren en het omzetten naar 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
}

Dit levert het volgende op:

{
  "data": {
    "postData": {
      "id": 1,
      "date": "2019-08-02T07:53:57",
      "type": "post",
      "title": {
        "rendered": "HELLO WORLD!"
      }
    }
  }
}

@underEachArrayItem

@underEachArrayItem itereert over de array-items van een bepaald veld in de opgevraagde entiteit en voert de geneste directive(s) uit op elk van hen.

Zo is het veld Post.categoryNames van het type [String]. Met @underEachArrayItem kunnen we de categorienamen itereren en de directive @strTranslate erop toepassen.

In deze query worden de berichtcategorieën vertaald van Engels naar Frans:

query {
  posts {
    id
    title
    categoryNames
      @underEachArrayItem
        @strTranslate(
          from: "en",
          to: "fr"
        )
  }
}

...wat het volgende oplevert:

{
  "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 kan zowel de index als de waarde van het geitereerde element als een dynamische variabele doorgeven aan de geneste directive(s), via de directive-argumenten passIndexOnwardsAs en passValueOnwardsAs.

Deze query demonstreert het gebruik van de dynamische variabelen $index en $value:

{
  _echo(value: ["first", "second", "third"])
    @underEachArrayItem(
      passIndexOnwardsAs: "index"
      passValueOnwardsAs: "value"
    )
      @applyField(
        name: "_echo"
        arguments: {
          value: {
            index: $index,
            value: $value
          }
        },
        setResultInResponse: true
      )
}

Het resultaat is:

{
  "data": {
    "_echo": [
      {
        "index": 0,
        "value": "first"
      },
      {
        "index": 1,
        "value": "second"
      },
      {
        "index": 2,
        "value": "third"
      }
    ]
  }
}

@underEachArrayItem kan ook de posities van de array beperken waarover geitereerd wordt, via de parameter filter->by, die ofwel de vermelding include of exclude kan accepteren.

Deze 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
}

...levert het volgende op:

{
  "data": {
    "including": [
      "FIRST",
      "second",
      "THIRD"
    ],
    "excluding": [
      "first",
      "SECOND",
      "third"
    ]
  }
}

@underEachJSONObjectProperty

@underEachJSONObjectProperty is vergelijkbaar met @underEachArrayItem, maar werkt op JSONObject-elementen.

In deze query itereren we over alle vermeldingen in het JSON-object en vervangen we elke null-vermelding door een lege tekenreeks:

{
  _echo(
    value: {
      first: "hello",
      second: "world",
      third: null
    }
  )
    @underEachJSONObjectProperty
      @default(value: "")
}

...wat het volgende oplevert:

{
  "data": {
    "_echo": {
      "first": "hello",
      "second": "world",
      "third": ""
    }
  }
}

@underEachJSONObjectProperty kan de sleutel en waarde waarover het itereert als een dynamische variabele doorgeven aan de geneste directive(s), via de directive-argumenten passKeyOnwardsAs en passValueOnwardsAs.

Deze query demonstreert het gebruik van de dynamische variabelen $key en $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
      )
}

Het resultaat is:

{
  "data": {
    "_echo": {
      "uno": {
        "key": "uno",
        "value": "first"
      },
      "dos": {
        "key": "dos",
        "value": "second"
      },
      "tres": {
        "key": "tres",
        "value": "third"
      }
    }
  }
}

@underEachJSONObjectProperty kan ook de sleutels van het JSON-object beperken waarover geitereerd wordt, via de parameter filter->by, die ofwel de vermelding includeKeys of excludeKeys kan accepteren.

Deze 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
}

...levert het volgende op:

{
  "data": {
    "includingKeys": {
      "uno": "FIRST",
      "dos": "second",
      "tres": "THIRD"
    },
    "excludingKeys": {
      "uno": "first",
      "dos": "SECOND",
      "tres": "third"
    }
  }
}

@objectClone

JSON-objecten kunnen per referentie worden benaderd in de veld-resolvers (en niet door het object te kopiëren/dupliceren). Wanneer dat het geval is, zal een wijziging aan het JSON-object zichtbaar zijn voor alle velden die dit JSON-object ophalen.

Dit is het geval bij het veld Block.attributes:

{
  posts {
    blocks(filterBy: { include: "core/heading" } ) {
      attributes
    }
  }
}

...wat het volgende oplevert:

{
  "data": {
    "posts": [
      {
        "blocks": [
          {
            "attributes": {
              "content": "Image Block (Full width)",
              "level": 2
            }
          },
          {
            "attributes": {
              "content": "Gallery Block",
              "level": 2
            }
          }
        ]
      }
    ]
  }
}

In de onderstaande query haalt originalAttributes simpelweg de attributen op, terwijl transformedAttributes ook de content-eigenschap naar het Frans vertaalt:

{
  posts {
    blocks(filterBy: { include: "core/heading" } ) {
      originalAttributes: attributes
      transformedAttributes: attributes
        @underJSONObjectProperty(by: { key: "content" })
          @strTranslate(to: "fr")
    }
  }
}

Omdat de opgevraagde Block-entiteit echter hetzelfde JSON-object gebruikt voor zowel originalAttributes als transformedAttributes, zullen de transformaties die door het laatste veld worden uitgevoerd ook het eerste veld beĂŻnvloeden (ongeacht de volgorde waarin ze in de query verschijnen).

Als gevolg hiervan worden beide velden naar het Frans vertaald:

{
  "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
            }
          }
        ]
      }
    ]
  }
}

We kunnen dit probleem vermijden door de directive @objectClone toe te voegen aan het veld transformedAttributes, zodat de wijzigingen worden uitgevoerd op een gekloond JSON-object:

{
  posts {
    blocks(filterBy: { include: "core/heading" } ) {
      originalAttributes: attributes
      transformedAttributes: attributes
        @objectClone
        @underJSONObjectProperty(by: { key: "content" })
          @strTranslate(to: "fr")
    }
  }
}

...wat het volgende oplevert:

{
  "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
            }
          }
        ]
      }
    ]
  }
}

Verdere voorbeelden

In deze query omhult @underEachArrayItem de @underJSONObjectProperty, die op zijn beurt @strUpperCase omhult, waardoor de eigenschap "title.rendered" naar hoofdletters wordt getransformeerd voor de meerdere berichtitems die via de WP REST API zijn verkregen:

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
}

...wat het volgende oplevert:

{
  "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"
        }
      }
    ]
  }
}