コンセプト、アイデア、戦略
コンセプト、アイデア、戦略動的に構造化されたデータの取得

動的に構造化されたデータの取得

WordPressでは、ネストされたレベルのデータ、つまり同じ型の子アイテムを含むエンティティを取得できます。たとえば、メニューにはサブアイテムを持てるアイテムが含まれており、そのサブアイテム自体もサブアイテムを含むことができ、これが複数のレベルにわたって続きます。同様に、コメントには返信があり、その返信自体にも返信があります。

GraphQLでメニューを扱う方法を見てみましょう。GraphQLでメニューデータを取得するには、さまざまなレベルにわたってメニュー内のアイテムをクエリする必要があります。たとえば、以下のクエリでは、メニューは3レベルあり、フラグメント MenuItemProps を使用して、すべてのレベルのメニューアイテムに対して同じフィールド(idlabelurl)を取得します。

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

ご覧のとおり、レベルの数はGraphQLクエリに反映されます。アプリケーション内のメニューが3レベルであるため、GraphQLクエリも3レベルのネストを持ちます。

ただし、WordPressではメニューの作成はあらかじめ決定されているわけではなく、サイトの管理者がメニュー画面(「ブロックテーマ」を使用していない場合)を通じて設定し、DBに保存されます。

WordPressでメニューを作成する

これは問題を引き起こします。ユーザーインターフェースを通じてメニューにレベルを追加した場合、GraphQLクエリにも追加レベルを加える必要があります。そうしないと、新しいレベルがサイトに表示されません。

この問題に対処する方法は2つあります。より簡単な方法は、最初から必要なレベルより多くのレベルを取得するGraphQLクエリを作成することで、後でレベルを追加し続ける余地を持たせます。たとえば、アプリケーションが3レベルを必要とする場合、GraphQLクエリは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
}

2つ目の解決策は、フィールド Menu.itemDataEntries を使用することで、すべてのレベルとサブレベルを含むメニューデータ全体を構造化した JSONObject として生成します。

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

このクエリへのレスポンスは次のようになります。

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

この方法には、取得されるデータがユーザーインターフェースによって完全に制御され、DBに保存されている内容をそのまま反映するという利点があります。そのため、メニューに追加レベル(2つであれ20つであれ)を追加しても、アプリケーションを更新する必要がありません。

ただし、この方法にはGraphQLの強い型付けが失われるという明確な欠点があります。urlURL 型、labelString 型、objectIDID 型として強く型付けされたフィールドを持つメニューアイテムを受け取る代わりに、Apollo clientやRelayのようなGraphQLツールやクライアントが理解できないプレーンなオブジェクトを取得することになります。したがって、GraphQLの利点を十分に活かすことができません。

WordPressの設定データの取得

もう一つの問題は、ユーザーインターフェースによって制御されDBに保存されているエンティティを取得する必要がある場合です。WordPressの設定がその例で、オプション名はテーマやプラグインによって動的に作成されるため、GraphQLサーバーには事前に知られておらず、テーマやプラグインによって定義される可能性のあるメタ値も同様に、デフォルトではGraphQLスキーマにマッピングされません。

このため、Gato GraphQLが生成するスキーマはオプション名とその型をハードコードせず、これらはオプションの名前を受け取り、任意の組み込み型の値(AnyBuiltInScalar で表される)を返す optionValue フィールド(および optionValuesoptionObjectValue)を通じてアクセスされます。

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

すべてのオプションがAPIを通じて公開されることを意図しているわけではないため、サイトの管理者はプラグインの設定でそれらを明示的に許可リストに追加する必要があります(完全な名前またはregexで)。

設定ページで許可リストにオプションを追加する
設定ページで許可リストにオプションを追加する

これで、クエリは許可リストに登録されたオプションを取得できます。

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

アプリケーションで必要な追加オプションがある場合は、設定ページの許可リストに対応するエントリを追加するだけで、すぐにAPIで利用可能にできます。