Ophalen van dynamisch gestructureerde gegevens
In WordPress kunnen we geneste niveaus van gegevens ophalen, d.w.z. entiteiten die onderliggende items van hetzelfde type bevatten. Een menu bevat bijvoorbeeld items die subitems kunnen hebben, en die subitems kunnen zelf ook weer subitems bevatten ā en zo verder voor meerdere niveaus. Op dezelfde manier kan een reactie antwoorden hebben die zelf ook weer antwoorden kunnen hebben.
Laten we kijken hoe we met menu's werken in GraphQL. Het ophalen van menugegevens in GraphQL houdt in dat je de items in het menu opvraagt voor alle verschillende niveaus. In de onderstaande query heeft het menu bijvoorbeeld 3 niveaus, en gebruiken we het fragment MenuItemProps om dezelfde velden (id, label en url) op te halen voor alle menu-items op alle niveaus:
query GetMenu {
menu(by: { id: 176 }) {
id
items {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
}
}
}
}
}
fragment MenuItemProps on MenuItem {
id
label
url
}Zoals je kunt zien, wordt het aantal niveaus weerspiegeld in de GraphQL-query. Omdat het menu in de applicatie 3 niveaus heeft, heeft de GraphQL-query 3 niveaus van nesting.
In WordPress wordt het aanmaken van het menu echter niet vooraf bepaald ā het wordt geconfigureerd door de sitebeheerder via het Menu-scherm (d.w.z. wanneer er geen "blokthema" wordt gebruikt) en opgeslagen in de database:

Dit geeft een probleem: als je een extra niveau aan het menu toevoegt via de gebruikersinterface, moet je ook een extra niveau toevoegen aan de GraphQL-query, anders wordt het nieuwe niveau niet weergegeven op de site.
Er zijn 2 manieren om dit probleem op te lossen. De eenvoudigste is om de GraphQL-query zoveel niveaus te laten ophalen als je initieel nodig hebt, plus wat extra ruimte om later meer niveaus toe te voegen. Als de applicatie bijvoorbeeld 3 niveaus nodig heeft, kan de GraphQL-query toch gegevens ophalen voor 6 (of 10 of 20) niveaus, zodat we genoeg ruimte hebben om het menu uit te breiden totdat we de limiet bereiken:
query GetMenu {
menu(by: { id: 176 }) {
id
items {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
}
}
}
}
}
}
}
}
fragment MenuItemProps on MenuItem {
id
label
url
}De tweede oplossing is het gebruik van het veld Menu.itemDataEntries, dat een gestructureerd JSONObject produceert met alle menugegevens, inclusief alle niveaus en subniveaus:
query GetMenu {
menu(by: { id: 176 }) {
id
itemDataEntries
}
}Het antwoord op deze query ziet er als volgt uit:
{
"data": {
"menu": {
"id": 176,
"itemDataEntries": [
{
"id": 735,
"objectID": "6",
"parentID": null,
"label": "About The Tests",
"url": "https://mywpsite.com/about/",
"children": [
{
"id": 1451,
"objectID": "1133",
"parentID": "735",
"label": "Page Image Alignment",
"url": "https://mywpsite.com/about/page-image-alignment/",
"children": []
},
{
"id": 1452,
"objectID": "1134",
"parentID": "735",
"label": "Page Markup And Formatting",
"url": "https://mywpsite.com/about/page-markup-and-formatting/",
"children": []
}
]
},
{
"id": 739,
"objectID": "174",
"parentID": null,
"label": "Level 1",
"url": "https://mywpsite.com/level-1/",
"children": [
{
"id": 740,
"objectID": "173",
"parentID": "739",
"label": "Level 2",
"url": "https://mywpsite.com/level-1/level-2/",
"children": [
{
"id": 741,
"objectID": "172",
"parentID": "740",
"label": "Level 3",
"url": "https://mywpsite.com/level-1/level-2/level-3/",
"children": []
},
{
"id": 1453,
"objectID": "747",
"parentID": "740",
"label": "Level 3a",
"url": "https://mywpsite.com/level-1/level-2/level-3a/",
"children": []
},
{
"id": 1454,
"objectID": "748",
"parentID": "740",
"label": "Level 3b",
"url": "https://mywpsite.com/level-1/level-2/level-3b/",
"children": []
}
]
}
]
},
{
"id": 742,
"objectID": "146",
"parentID": null,
"label": "Lorem Ipsum",
"url": "https://mywpsite.com/lorem-ipsum/",
"children": []
}
]
}
}
}Deze methode heeft als voordeel dat de opgehaalde gegevens volledig worden bepaald door de gebruikersinterface en precies weerspiegelen wat er in de database is opgeslagen ā zodat de applicatie nooit bijgewerkt hoeft te worden als er extra niveaus aan het menu worden toegevoegd, of het nu 2 of 20 niveaus zijn.
Deze methode heeft echter het duidelijke nadeel dat we het sterke typeren van GraphQL verliezen. In plaats van een menu-item te ontvangen met sterk getypeerde velden zoals url als URL, label als String, objectID als ID, enzovoort, krijgen we een gewoon object dat niet begrepen wordt door GraphQL-tools en -clients, zoals Apollo client of Relay. We maken daardoor niet optimaal gebruik van de voordelen van GraphQL.
WordPress-instellingsgegevens ophalen
Een ander probleem doet zich voor wanneer we entiteiten moeten ophalen die worden bepaald door de gebruikersinterface en opgeslagen in de database. Dat is het geval met instellingen in WordPress, waarbij de namen van de opties dynamisch worden aangemaakt door thema's en plugins ā en dus niet vooraf bekend zijn bij de GraphQL-server. Hetzelfde geldt voor meta-waarden, die ook door thema's en plugins kunnen worden gedefinieerd, en daarom niet standaard worden toegewezen aan het GraphQL-schema.
Om die reden legt het schema dat door Gato GraphQL wordt gegenereerd de optienames en hun typen niet vast, maar zijn ze toegankelijk via een optionValue-veld (en ook optionValues en optionObjectValue) dat de naam van de optie ontvangt en een waarde teruggeeft van elk mogelijk ingebouwd type (weergegeven door AnyBuiltInScalar):
type Root {
optionValue(name: String!): AnyBuiltInScalar
}Omdat niet alle opties via de API beschikbaar gesteld hoeven te worden, moet de sitebeheerder ze expliciet toevoegen aan de allowlist ā op volledige naam of via een regex ā in de plugin-instellingen:

Nu kan de query de opties op de allowlist ophalen:
{
siteURL: optionValue(name: "siteurl")
siteName: optionValue(name: "blogname")
siteDescription: optionValue(name: "blogdescription")
}Als de applicatie een extra optie nodig heeft, kan deze direct beschikbaar worden gesteld aan de API door een bijbehorend item toe te voegen aan de allowlist op de instellingenpagina.