概念、想法、策略
概念、想法、策略获取动态结构化数据

获取动态结构化数据

在 WordPress 中,我们可以获取嵌套层级的数据,即包含同类型子项的实体。例如,菜单包含可拥有子项的条目,而那些子项本身也可以包含子项,并以此类推延伸多个层级。类似地,评论可以有回复,而这些回复本身也可以有回复。

让我们来看看如何在 GraphQL 中处理菜单。在 GraphQL 中获取菜单数据,需要针对所有不同层级查询菜单内的条目。例如,在下面的 query 中,菜单有 3 个层级,我们使用片段 MenuItemProps 为所有层级的菜单条目获取相同的字段(idlabelurl):

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

可以看出,层级数量会体现在 GraphQL query 中。由于应用程序中的菜单有 3 个层级,GraphQL query 也相应地有 3 层嵌套。

然而,在 WordPress 中,菜单的创建并非事先确定,而是由站点管理员通过菜单页面(即未使用"区块主题"时)进行配置并存储在数据库中:

在 WordPress 中创建菜单

这带来了一个问题:当通过用户界面为菜单添加额外层级时,我们也必须在 GraphQL query 中添加相应层级,否则新层级将无法在站点上显示。

解决这个问题有两种方式。较简单的方法是创建一个获取层级数超过初始需求的 GraphQL query,以便为后续添加层级预留空间。例如,如果应用程序需要 3 个层级,GraphQL query 可以获取 6(甚至 10 或 20)个层级的数据,为我们提供足够的空间来扩展菜单直至达到上限:

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
}

第二种解决方案是使用字段 Menu.itemDataEntries,它会将包含所有层级和子层级的完整菜单数据生成为结构化的 JSONObject

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

此 query 的响应如下所示:

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

这种方法的优点在于,所获取的数据完全由用户界面驱动,如实反映数据库中存储的内容,因此无论向菜单添加多少个额外层级(2 个还是 20 个),应用程序都无需更新。

然而,这种方法有一个明显的缺点:我们失去了 GraphQL 的强类型特性。我们不再获得具有强类型字段的菜单条目(例如 urlURL 类型、labelString 类型、objectIDID 类型等),而是获得一个普通对象,GraphQL 工具和客户端(如 Apollo client 或 Relay)无法理解它。因此,我们将无法充分发挥 GraphQL 的优势。

获取 WordPress 设置数据

另一个问题是当我们需要获取由用户界面驱动并存储在数据库中的实体时。WordPress 中的设置就是如此——选项名称由主题和插件动态创建,因此 GraphQL 服务器事先并不知晓这些名称;元值同样可以由主题和插件定义,因此默认情况下不会映射到 GraphQL schema 中。

出于这个原因,Gato GraphQL 生成的 schema 不会硬编码选项名称及其类型,而是通过接收选项名称的 optionValue 字段(以及 optionValuesoptionObjectValue)来访问这些选项,并返回任意内置类型的值(以 AnyBuiltInScalar 表示):

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

由于并非所有选项都需要通过 API 公开,站点管理员必须在插件设置中通过完整名称或正则表达式将其明确添加到白名单中:

在设置页面中将选项添加到白名单
在设置页面中将选项添加到白名单

现在,query 可以获取已加入白名单的选项:

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

如果应用程序需要额外的选项,只需在设置页面的白名单中添加相应条目,即可立即通过 API 使用。