Concepts, idées, stratégies
Concepts, idées, stratégiesObtenir des données à structure dynamique

Obtenir des données à structure dynamique

Dans WordPress, nous pouvons récupérer des niveaux imbriqués de données, c'est-à-dire des entités contenant des éléments enfants du même type. Par exemple, un menu contient des éléments qui peuvent avoir des sous-éléments, et ces sous-éléments peuvent eux-mêmes contenir des sous-éléments, et ainsi de suite pour plusieurs niveaux. De même, un commentaire peut avoir des réponses qui peuvent, elles-mêmes, avoir des réponses.

Voyons comment travailler avec les menus en GraphQL. La récupération des données de menu en GraphQL implique d'interroger les éléments à l'intérieur du menu pour tous les différents niveaux. Par exemple, dans la requête ci-dessous, le menu comporte 3 niveaux, et nous utilisons le fragment MenuItemProps pour récupérer les mêmes champs (id, label et url) pour tous les éléments de menu à tous les niveaux :

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

Comme on peut le constater, le nombre de niveaux se reflète dans la requête GraphQL. Puisque le menu de l'application comporte 3 niveaux, la requête GraphQL comporte 3 niveaux d'imbrication.

Cependant, dans WordPress, la création du menu n'est pas décidée à l'avance, mais elle est configurée par l'administrateur du site via l'écran Menus (c'est-à-dire lorsqu'on n'utilise pas un « block theme »), et stockée en base de données :

Créer des menus dans WordPress

Cela pose un problème : lors de l'ajout d'un niveau supplémentaire au menu via l'interface utilisateur, nous devons également ajouter un niveau supplémentaire à la requête GraphQL, sinon le nouveau niveau ne sera pas affiché sur le site.

Il y a 2 façons de traiter ce problème. La plus simple est de créer la requête GraphQL en récupérant plus de niveaux que nécessaire initialement, afin d'avoir de la marge pour continuer à ajouter des niveaux par la suite. Par exemple, si l'application a besoin de 3 niveaux, la requête GraphQL pourrait néanmoins récupérer des données pour 6 (ou 10 ou 20) niveaux, nous donnant suffisamment d'espace pour étendre le menu jusqu'à atteindre la limite :

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
}

La deuxième solution est d'utiliser le champ Menu.itemDataEntries qui produira un JSONObject structuré avec l'ensemble des données du menu, incluant tous les niveaux et sous-niveaux :

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

La réponse à cette requête ressemble à ceci :

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

Cette méthode présente l'avantage que les données récupérées sont entièrement pilotées par l'interface utilisateur, reflétant ce qui est stocké en base de données tel quel, de sorte que l'application n'aurait jamais besoin d'être mise à jour lors de l'ajout de niveaux supplémentaires au menu, qu'il s'agisse de 2 ou de 20 niveaux.

Cependant, cette méthode présente le clair inconvénient de perdre le typage fort de GraphQL : au lieu de recevoir un élément de menu avec des champs fortement typés url en tant qu'URL, label en tant que String, objectID en tant qu'ID, et ainsi de suite, nous obtenons un objet simple qui ne sera pas compris par les outils et clients GraphQL, comme Apollo client ou Relay. Nous ne tirerons donc pas réellement parti des avantages de GraphQL.

Récupérer les données de configuration de WordPress

Un autre problème se pose lorsque nous devons récupérer des entités pilotées par l'interface utilisateur et stockées en base de données. C'est le cas avec les options dans WordPress, où les noms des options sont créés dynamiquement par les thèmes et les plugins, de sorte qu'ils ne sont pas connus à l'avance par le serveur GraphQL, et les valeurs meta, qui peuvent également être définies par les thèmes et les plugins, et ne sont donc pas par défaut mappées au schéma GraphQL.

Pour cette raison, le schéma produit par Gato GraphQL ne code pas en dur les noms des options ni leurs types, mais on y accède via un champ optionValue (et aussi optionValues et optionObjectValue) qui reçoit le nom de l'option et retourne une valeur de n'importe quel type intégré possible (représenté par AnyBuiltInScalar) :

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

Toutes les options n'étant pas destinées à être exposées via l'API, l'administrateur du site doit les ajouter explicitement à la liste d'autorisation, soit par leur nom complet, soit par une expression régulière, dans les paramètres du plugin :

Ajout d'options à la liste d'autorisation dans la page des paramètres
Ajout d'options à la liste d'autorisation dans la page des paramètres

Désormais, la requête peut récupérer les options figurant sur la liste d'autorisation :

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

S'il existe une option supplémentaire dont l'application a besoin, elle peut être immédiatement mise à disposition de l'API en ajoutant simplement une entrée correspondante à la liste d'autorisation dans la page des paramètres.