Conceitos, Ideias, Estratégias
Conceitos, Ideias, EstratégiasObtendo dados com estrutura dinâmica

Obtendo dados com estrutura dinâmica

No WordPress, podemos buscar níveis aninhados de dados, ou seja, entidades que contêm itens filhos do mesmo tipo. Por exemplo, um menu contém itens que podem ter subitens, e esses subitens podem, por sua vez, conter subitens, e assim por diante em vários níveis. Da mesma forma, um comentário pode ter respostas que, por sua vez, também podem ter respostas.

Vamos ver como trabalhar com menus no GraphQL. Buscar os dados do menu no GraphQL envolve consultar os itens dentro do menu em todos os diferentes níveis. Por exemplo, na query abaixo, o menu tem 3 níveis, e usamos o fragment MenuItemProps para buscar os mesmos campos (id, label e url) para todos os itens do menu em todos os níveis:

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

Como se pode notar, o número de níveis se reflete na query GraphQL. Como o menu da aplicação tem 3 níveis, a query GraphQL tem 3 níveis de aninhamento.

No entanto, no WordPress a criação do menu não é definida com antecedência, mas é configurada pelo administrador do site por meio da tela de Menus (ou seja, quando não se usa um "block theme"), e armazenada no banco de dados:

Criando menus no WordPress

Isso apresenta um problema: ao adicionar um nível extra ao menu por meio da interface do usuário, também precisamos adicionar um nível extra à query GraphQL, caso contrário o novo nível não será exibido no site.

Há 2 maneiras de lidar com esse problema. A mais simples é criar a query GraphQL buscando mais níveis do que os necessários inicialmente, de modo que haja espaço para continuar adicionando níveis posteriormente. Por exemplo, se a aplicação precisa de 3 níveis, a query GraphQL poderia mesmo assim buscar dados para 6 (ou 10 ou 20) níveis, dando espaço suficiente para expandir o menu até atingir o 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
}

A segunda solução é usar o campo Menu.itemDataEntries, que produzirá um JSONObject estruturado com todos os dados do menu, incluindo todos os níveis e subníveis:

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

A resposta a essa query se parece com isto:

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

Esse método tem a vantagem de que os dados recuperados são completamente determinados pela interface do usuário, refletindo exatamente o que está armazenado no banco de dados, de modo que a aplicação nunca precisaria ser atualizada ao adicionar níveis extras ao menu, sejam 2 ou 20.

No entanto, esse método tem a clara desvantagem de perdermos a tipagem forte do GraphQL: em vez de receber um item de menu com campos fortemente tipados como url sendo uma URL, label sendo uma String, objectID sendo um ID, e assim por diante, obtemos um objeto simples que não será compreendido pelas ferramentas e clientes GraphQL, como Apollo client ou Relay. Portanto, não aproveitaremos de fato ao máximo os benefícios do GraphQL.

Buscando dados de configurações do WordPress

Outro problema surge quando precisamos buscar entidades que são determinadas pela interface do usuário e armazenadas no banco de dados. É o caso das configurações no WordPress, onde os nomes das opções são criados dinamicamente por temas e plugins, portanto não são conhecidos antecipadamente pelo servidor GraphQL, e dos valores meta, que também podem ser definidos por temas e plugins e, por isso, não são mapeados por padrão no schema GraphQL.

Por esse motivo, o schema produzido pelo Gato GraphQL não codifica rigidamente os nomes das opções nem seus tipos, mas eles são acessados por meio de um campo optionValue (e também optionValues e optionObjectValue) que recebe o nome da opção e retorna um valor de qualquer tipo embutido possível (representado por AnyBuiltInScalar):

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

Como nem todas as opções devem ser expostas pela API, o administrador do site deve adicioná-las explicitamente à lista de permissões, seja pelo nome completo ou por uma expressão regular, nas configurações do plugin:

Adicionando opções à lista de permissões na página de configurações
Adicionando opções à lista de permissões na página de configurações

Agora, a query pode buscar as opções presentes na lista de permissões:

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

Se houver uma opção extra necessária para a aplicação, ela pode ser disponibilizada imediatamente na API simplesmente adicionando uma entrada correspondente à lista de permissões na página de configurações.