Lấy dữ liệu có cấu trúc động
Trong WordPress, chúng ta có thể lấy các cấp dữ liệu lồng nhau, tức là các thực thể chứa các mục con cùng kiểu. Ví dụ, một menu chứa các mục có thể có các mục con, và các mục con đó có thể chứa các mục con khác, cứ như vậy cho nhiều cấp. Tương tự, một bình luận có thể có các phản hồi, và các phản hồi đó cũng có thể có phản hồi riêng.
Hãy cùng xem cách làm việc với menu trong GraphQL. Việc lấy dữ liệu menu trong GraphQL đòi hỏi phải truy vấn các mục bên trong menu cho tất cả các cấp khác nhau. Ví dụ, trong queries bên dưới, menu có 3 cấp và chúng ta dùng fragment MenuItemProps để lấy các trường giống nhau (id, label và url) cho tất cả các mục menu ở mọi cấp:
query GetMenu {
menu(by: { id: 176 }) {
id
items {
...MenuItemProps
children {
...MenuItemProps
children {
...MenuItemProps
}
}
}
}
}
fragment MenuItemProps on MenuItem {
id
label
url
}Như có thể thấy, số cấp được phản ánh trong queries GraphQL. Vì menu trong ứng dụng có 3 cấp, nên queries GraphQL cũng có 3 cấp lồng nhau.
Tuy nhiên, trong WordPress việc tạo menu không được quyết định trước, mà được cấu hình bởi quản trị viên trang web thông qua màn hình Menu (tức là khi không sử dụng "block theme"), và được lưu trữ trong cơ sở dữ liệu:

Điều này tạo ra một vấn đề: khi thêm một cấp bổ sung vào menu qua giao diện người dùng, chúng ta cũng phải thêm một cấp bổ sung vào queries GraphQL, nếu không cấp mới sẽ không được hiển thị trên trang.
Có 2 cách để giải quyết vấn đề này. Cách đơn giản hơn là tạo queries GraphQL lấy nhiều cấp hơn mức cần thiết ban đầu, để có chỗ tiếp tục thêm các cấp sau này. Ví dụ, nếu ứng dụng cần 3 cấp, queries GraphQL có thể lấy dữ liệu cho 6 (hoặc 10 hoặc 20) cấp, cho chúng ta đủ không gian để mở rộng menu cho đến khi đạt giới hạn:
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
}Giải pháp thứ hai là sử dụng trường Menu.itemDataEntries sẽ tạo ra một JSONObject có cấu trúc với toàn bộ dữ liệu menu, bao gồm tất cả các cấp và cấp con:
query GetMenu {
menu(by: { id: 176 }) {
id
itemDataEntries
}
}Phản hồi cho queries này trông như sau:
{
"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": []
}
]
}
}
}Phương pháp này có ưu điểm là dữ liệu được lấy hoàn toàn do giao diện người dùng điều khiển, phản ánh đúng những gì được lưu trong cơ sở dữ liệu, nên ứng dụng sẽ không bao giờ cần cập nhật khi thêm các cấp bổ sung vào menu, dù là 2 hay 20 cấp.
Tuy nhiên, phương pháp này có nhược điểm rõ ràng là chúng ta mất đi hệ thống kiểu dữ liệu mạnh của GraphQL: thay vì nhận một mục menu với các trường được định kiểu chặt chẽ như url là URL, label là String, objectID là ID, v.v., chúng ta nhận được một đối tượng thuần túy mà các công cụ và client GraphQL như Apollo client hay Relay sẽ không hiểu. Do đó, chúng ta sẽ không thực sự tận dụng hết những lợi ích của GraphQL.
Lấy dữ liệu cài đặt WordPress
Một vấn đề khác là khi chúng ta cần lấy các thực thể được điều khiển bởi giao diện người dùng và lưu trong cơ sở dữ liệu. Đó là trường hợp của các cài đặt trong WordPress, nơi tên của các tùy chọn được tạo động bởi các theme và plugin, vì vậy chúng không được biết trước bởi máy chủ GraphQL, và các giá trị meta cũng có thể được định nghĩa bởi các theme và plugin nên mặc định không được ánh xạ vào schema GraphQL.
Vì lý do này, schema được tạo bởi Gato GraphQL không mã hóa cứng tên các tùy chọn và kiểu dữ liệu của chúng, mà thay vào đó chúng được truy cập qua trường optionValue (cũng như optionValues và optionObjectValue) nhận tên của tùy chọn và trả về giá trị thuộc bất kỳ kiểu dựng sẵn nào (được biểu diễn bởi AnyBuiltInScalar):
type Root {
optionValue(name: String!): AnyBuiltInScalar
}Vì không phải tất cả các tùy chọn đều được phơi bày qua API, quản trị viên trang web phải thêm chúng một cách rõ ràng vào danh sách cho phép, theo tên đầy đủ hoặc regex, trong phần cài đặt plugin:

Bây giờ, queries có thể lấy các tùy chọn đã được đưa vào danh sách trắng:
{
siteURL: optionValue(name: "siteurl")
siteName: optionValue(name: "blogname")
siteDescription: optionValue(name: "blogdescription")
}Nếu có một tùy chọn bổ sung mà ứng dụng cần, nó có thể được cung cấp ngay cho API chỉ bằng cách thêm một mục tương ứng vào danh sách cho phép trong trang Cài đặt.