Konzepte, Ideen, Strategien
Konzepte, Ideen, StrategienDynamisch strukturierte Daten abrufen

Dynamisch strukturierte Daten abrufen

In WordPress können wir verschachtelte Datenebenen abrufen, d. h. Entitäten, die untergeordnete Elemente desselben Typs enthalten. Ein Menü enthält beispielsweise Elemente, die Unterelemente haben können, und diese Unterelemente können selbst wieder Unterelemente enthalten, und so weiter über mehrere Ebenen. Ebenso kann ein Kommentar Antworten haben, die ihrerseits Antworten haben können.

Schauen wir uns an, wie wir mit Menüs in GraphQL arbeiten. Das Abrufen der Menüdaten in GraphQL bedeutet, die Elemente innerhalb des Menüs für alle verschiedenen Ebenen abzufragen. In der folgenden query hat das Menü beispielsweise 3 Ebenen, und wir verwenden das Fragment MenuItemProps, um dieselben Felder (id, label und url) für alle Menüelemente auf allen Ebenen abzurufen:

query GetMenu {
  menu(by: { id: 176 }) {
    id
    items {
      ...MenuItemProps
      children {
        ...MenuItemProps
        children {
          ...MenuItemProps
        }
      }
    }
  }
}
 
fragment MenuItemProps on MenuItem {
  id
  label
  url
}

Wie zu erkennen ist, spiegelt sich die Anzahl der Ebenen in der GraphQL-query wider. Da das Menü der Anwendung 3 Ebenen hat, hat die GraphQL-query 3 Verschachtelungsebenen.

In WordPress wird die Erstellung des Menüs jedoch nicht im Voraus festgelegt, sondern vom Site-Administrator über den Menü-Bildschirm konfiguriert (d. h. wenn kein „Block-Theme" verwendet wird) und in der Datenbank gespeichert:

Menüs in WordPress erstellen

Das stellt ein Problem dar: Wenn über die Benutzeroberfläche eine zusätzliche Ebene zum Menü hinzugefügt wird, muss auch eine zusätzliche Ebene zur GraphQL-query hinzugefügt werden, da sonst die neue Ebene nicht auf der Website angezeigt wird.

Es gibt 2 Möglichkeiten, mit diesem Problem umzugehen. Die einfachere besteht darin, die GraphQL-query so zu erstellen, dass mehr Ebenen als anfänglich benötigt abgerufen werden, sodass später noch weitere Ebenen hinzugefügt werden können. Wenn die Anwendung beispielsweise 3 Ebenen benötigt, könnte die GraphQL-query dennoch Daten für 6 (oder 10 oder 20) Ebenen abrufen und uns genug Spielraum lassen, das Menü zu erweitern, bis wir das Limit erreichen:

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
}

Die zweite Lösung besteht darin, das Feld Menu.itemDataEntries zu verwenden, das ein strukturiertes JSONObject mit der Gesamtheit der Menüdaten erzeugt, einschließlich aller Ebenen und Unterebenen:

query GetMenu {
  menu(by: { id: 176 }) {
    id
    itemDataEntries
  }
}

Die Antwort auf diese query sieht so aus:

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

Diese Methode hat den Vorteil, dass die abgerufenen Daten vollständig durch die Benutzeroberfläche gesteuert werden und genau das widerspiegeln, was in der Datenbank gespeichert ist. Die Anwendung müsste also nie aktualisiert werden, wenn dem Menü zusätzliche Ebenen hinzugefügt werden, egal ob 2 oder 20.

Diese Methode hat jedoch den klaren Nachteil, dass wir die starke Typisierung von GraphQL verlieren: Anstatt ein Menüelement mit stark typisierten Feldern zu erhalten – url als URL, label als String, objectID als ID usw. –, erhalten wir ein einfaches Objekt, das von GraphQL-Tools und -Clients wie Apollo client oder Relay nicht verstanden wird. Wir nutzen die Vorteile von GraphQL also nicht wirklich aus.

WordPress-Einstellungsdaten abrufen

Ein weiteres Problem entsteht, wenn wir Entitäten abrufen müssen, die durch die Benutzeroberfläche gesteuert und in der Datenbank gespeichert werden. Das ist bei den Einstellungen in WordPress der Fall, wo die Namen der Optionen dynamisch von Themes und Plugins erstellt werden und daher dem GraphQL-Server nicht im Voraus bekannt sind, sowie bei Meta-Werten, die ebenfalls von Themes und Plugins definiert werden können und daher standardmäßig nicht im GraphQL-Schema abgebildet sind.

Aus diesem Grund kodiert das von Gato GraphQL erzeugte Schema die Optionsnamen und ihre Typen nicht fest, sondern der Zugriff erfolgt über ein Feld optionValue (sowie optionValues und optionObjectValue), das den Namen der Option entgegennimmt und einen Wert eines möglichen eingebauten Typs zurückgibt (dargestellt durch AnyBuiltInScalar):

type Root {
  optionValue(name: String!): AnyBuiltInScalar
}

Da nicht alle Optionen über die API verfügbar gemacht werden sollen, muss der Site-Administrator sie entweder über ihren vollständigen Namen oder einen regulären Ausdruck explizit zur Erlaubnisliste in den Plugin-Einstellungen hinzufügen:

Optionen zur Erlaubnisliste auf der Einstellungsseite hinzufügen
Optionen zur Erlaubnisliste auf der Einstellungsseite hinzufügen

Jetzt kann die query die auf der Erlaubnisliste stehenden Optionen abrufen:

{
  siteURL: optionValue(name: "siteurl")
  siteName: optionValue(name: "blogname")
  siteDescription: optionValue(name: "blogdescription")
}

Wenn eine zusätzliche Option benötigt wird, kann sie der API sofort verfügbar gemacht werden, indem einfach ein entsprechender Eintrag zur Erlaubnisliste auf der Einstellungsseite hinzugefügt wird.