Konsep, Ide, Strategi
Konsep, Ide, StrategiMengambil data berstruktur dinamis

Mengambil data berstruktur dinamis

Di WordPress, kita dapat mengambil level data bersarang, yaitu entitas yang berisi item anak dengan tipe yang sama. Misalnya, sebuah menu berisi item yang dapat memiliki subitem, dan subitem tersebut dapat berisi subitem lagi, begitu seterusnya untuk beberapa level. Demikian pula, sebuah komentar dapat memiliki balasan yang, pada gilirannya, dapat memiliki balasan sendiri.

Mari kita lihat cara bekerja dengan menu di GraphQL. Mengambil data menu di GraphQL melibatkan query item di dalam menu untuk semua level yang berbeda. Misalnya, dalam query di bawah ini, menu memiliki 3 level, dan kita menggunakan fragment MenuItemProps untuk mengambil field yang sama (id, label, dan url) untuk semua item menu di semua level:

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

Seperti yang dapat dilihat, jumlah level tercermin dalam query GraphQL. Karena menu dalam aplikasi memiliki 3 level, query GraphQL memiliki 3 level penumpukan.

Namun, di WordPress pembuatan menu tidak ditentukan terlebih dahulu, melainkan dikonfigurasi oleh admin situs melalui layar Menu (yaitu, ketika tidak menggunakan "block theme"), dan disimpan di DB:

Membuat menu di WordPress

Ini menimbulkan masalah: saat menambahkan level ekstra ke menu melalui antarmuka pengguna, kita juga harus menambahkan level ekstra ke query GraphQL, atau level baru tersebut tidak akan ditampilkan di situs.

Ada 2 cara untuk menangani masalah ini. Yang lebih sederhana adalah membuat query GraphQL yang mengambil lebih banyak level dari yang dibutuhkan awalnya, sehingga ada ruang untuk terus menambahkan level nantinya. Misalnya, jika aplikasi membutuhkan 3 level, query GraphQL dapat tetap mengambil data untuk 6 (atau 10 atau 20) level, memberikan kita cukup ruang untuk memperluas menu hingga mencapai batas:

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
}

Solusi kedua adalah menggunakan field Menu.itemDataEntries yang akan menghasilkan JSONObject terstruktur dengan seluruh data menu, termasuk semua level dan sublevel:

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

Respons terhadap query ini terlihat seperti ini:

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

Metode ini memiliki keunggulan bahwa data yang diambil sepenuhnya dikendalikan oleh antarmuka pengguna, mencerminkan apa yang tersimpan di DB apa adanya, sehingga aplikasi tidak perlu diperbarui saat menambahkan level ekstra ke menu, baik 2 maupun 20 level.

Namun, metode ini memiliki kelemahan yang jelas bahwa kita kehilangan strong typing dari GraphQL: alih-alih menerima item menu dengan field yang diketik dengan kuat seperti url sebagai URL, label sebagai String, objectID sebagai ID, dan seterusnya, kita mendapatkan objek biasa yang tidak akan dipahami oleh alat dan klien GraphQL, seperti Apollo client atau Relay. Oleh karena itu, kita tidak akan benar-benar memaksimalkan manfaat GraphQL.

Mengambil data pengaturan WordPress

Masalah lain adalah ketika kita perlu mengambil entitas yang dikendalikan oleh antarmuka pengguna dan disimpan di DB. Itulah yang terjadi dengan pengaturan di WordPress, di mana nama opsi dibuat secara dinamis oleh tema dan plugin, sehingga tidak diketahui terlebih dahulu oleh server GraphQL, dan nilai meta, yang juga dapat didefinisikan oleh tema dan plugin, sehingga secara default tidak dipetakan ke skema GraphQL.

Untuk alasan ini, skema yang dihasilkan oleh Gato GraphQL tidak meng-hardcode nama opsi dan tipenya, melainkan diakses melalui field optionValue (dan juga optionValues dan optionObjectValue) yang menerima nama opsi, dan mengembalikan nilai dari tipe built-in apa pun yang memungkinkan (sebagaimana direpresentasikan oleh AnyBuiltInScalar):

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

Karena tidak semua opsi dimaksudkan untuk diekspos melalui API, admin situs harus menambahkannya secara eksplisit ke allowlist, baik berdasarkan nama lengkap maupun regex, di pengaturan plugin:

Menambahkan opsi ke allowlist di halaman Pengaturan
Menambahkan opsi ke allowlist di halaman Pengaturan

Sekarang, query dapat mengambil opsi yang ada di whitelist:

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

Jika ada opsi tambahan yang dibutuhkan oleh aplikasi, opsi tersebut dapat segera tersedia untuk API hanya dengan menambahkan entri yang sesuai ke allowlist di halaman Pengaturan.