Làm việc với
Làm việc vớiCác khối (Gutenberg)

Các khối (Gutenberg)

Đây là cách truy xuất dữ liệu khối Gutenberg.

Schema GraphQL có các trường sau được thêm vào tất cả các kiểu CustomPost (như PostPage):

  • blocks
  • blockDataItems
  • blockFlattenedDataItems

Các trường này không được kích hoạt nếu plugin Classic Editor đang hoạt động.

blocks

Trường CustomPost.blocks: [BlockUnion!] truy xuất danh sách tất cả các khối có trong custom post.

blocks trả về một Danh sách các kiểu Block đã được ánh xạ vào schema GraphQL. Các kiểu Block này đều là một phần của kiểu BlockUnion và triển khai giao diện Block.

Plugin triển khai một kiểu Block, GenericBlock, đã đủ để truy xuất dữ liệu cho bất kỳ khối nào (thông qua trường attributes: JSONObject).

Query này:

{
  post(by: { id: 1 }) {
    blocks {
      ...on Block {
        name
        attributes
        innerBlocks {
          ...on Block {
              name
              attributes
              innerBlocks {
                ...on Block {
                  name
                  attributes
                }
              }
            }
          }
        }
      }
    }
  }
}

...sẽ tạo ra phản hồi này:

{
  "data": {
    "post": {
      "blocks": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/list",
          "attributes": {
            "ordered": false,
            "values": "<li>List item 1</li><li>List item 2</li><li>List item 3</li><li>List item 4</li>"
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  },
                  "innerBlocks": null
                }
              ]
            },
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/paragraph",
                  "attributes": {
                    "className": "layout-column-2",
                    "content": "Phosfluorescently morph intuitive relationships rather than customer directed human capital.",
                    "dropCap": false
                  },
                  "innerBlocks": null
                }
              ]
            }
          ]
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  },
                  "innerBlocks": null
                },
                {
                  "name": "core/columns",
                  "attributes": {
                    "isStackedOnMobile": true
                  },
                  "innerBlocks": [
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "33.33%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/heading",
                          "attributes": {
                            "fontSize": "large",
                            "content": "Life is so rich",
                            "level": 2
                          },
                          "innerBlocks": null
                        },
                        {
                          "name": "core/heading",
                          "attributes": {
                            "level": 3,
                            "content": "Life is so dynamic"
                          },
                          "innerBlocks": null
                        }
                      ]
                    },
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "66.66%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/paragraph",
                          "attributes": {
                            "content": "This rhyming poem is the spark that can reignite the fires within you. It challenges you to go out and live your life in the present moment as a \u201chero\u201d and leave your mark on this world.",
                            "dropCap": false
                          },
                          "innerBlocks": null
                        },
                        {
                          "name": "core/columns",
                          "attributes": {
                            "isStackedOnMobile": true
                          },
                          "innerBlocks": [
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 361,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/graphql-voyager-public-1024x622.jpg",
                                    "alt": ""
                                  },
                                  "innerBlocks": null
                                }
                              ]
                            },
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": null
                            },
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 362,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/namespaced-interactive-schema-1024x598.webp",
                                    "alt": ""
                                  },
                                  "innerBlocks": null
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

Schema GraphQL cho các kiểu Block trông như sau:

interface Block {
  name: String!
  attributes: JSONObject
  innerBlocks: [BlockUnion!]
  contentSource: HTML!
}
 
type GenericBlock implements Block {
  name: String!
  attributes: JSONObject
  innerBlocks: [BlockUnion!]
  contentSource: HTML!
}
 
union BlockUnion = GenericBlock

Block fields

Giao diện Block (và do đó, kiểu GeneralBlock) chứa các trường sau:

  • name truy xuất tên của khối: "core/paragraph", "core/heading" "core/image", v.v.
  • attributes truy xuất một đối tượng JSON chứa tất cả các thuộc tính của khối.
  • innerBlocks truy xuất [BlockUnion!], do đó chúng ta có thể truy vấn nó để điều hướng qua cấu trúc phân cấp của các khối chứa các khối bên trong, và lấy dữ liệu cho tất cả chúng, cho bao nhiêu cấp độ sâu tùy ý trong nội dung.
  • contentSource truy xuất mã nguồn HTML (Gutenberg) của khối, bao gồm các ký tự phân cách nhận xét chứa các thuộc tính. Tuy nhiên, trường này không truy xuất dữ liệu hoàn toàn giống như cách lưu trữ trong DB (xem #2346), vì vậy hãy sử dụng trường này cẩn thận.

Directly retrieving GeneralBlock (instead of BlockUnion)

Vì hiện tại chỉ có một kiểu Block ánh xạ các khối –GeneralBlock– nên hợp lý khi để CustomPost.blocks (và cũng Block.innerBlocks) truy xuất kiểu này trực tiếp, thay vì kiểu BlockUnion.

Chúng ta có thể thực hiện điều này trong trang Cài đặt dưới tab Blocks, bằng cách tích vào tùy chọn Use single type instead of union type?:

Cấu hình để truy xuất trực tiếp `GeneralBlock` thay vì `BlockUnion`
Cấu hình để truy xuất trực tiếp `GeneralBlock` thay vì `BlockUnion`

Sau đó, query GraphQL được đơn giản hóa:

{
  post(by: { id: 1 }) {
    blocks {
      name
      attributes
      innerBlocks {
        name
        attributes
      }
    }
  }
}

Lưu ý rằng việc giữ kiểu phản hồi là BlockUnion có lợi cho khả năng tương thích ngược: Nếu chúng ta quyết định thêm các kiểu dành riêng cho khối vào schema (xem phần bên dưới), thì sẽ không có thay đổi phá vỡ nào.

Mapping block-specific types

Kiểu JSONObject (được truy xuất bởi Block.attributes) không được định kiểu chặt chẽ: Các thuộc tính của nó có thể có bất kỳ kiểu và lực lượng nào (String, Int, [Boolean!], v.v.), vì vậy chúng ta cần biết thông tin này cho mỗi khối và xử lý từng trường hợp ở phía client.

Nếu chúng ta cần định kiểu chặt chẽ, chúng ta phải mở rộng schema GraphQL thông qua mã PHP, thêm các kiểu dành riêng cho khối ánh xạ các thuộc tính cụ thể của khối thành các trường, và làm cho chúng trở thành một phần của BlockUnion.

Ví dụ, chúng ta có thể thêm kiểu CoreParagraphBlock ánh xạ khối core/paragraph, với trường content có kiểu String.

Tham khảo tài liệu tại GatoGraphQL/GatoGraphQL để tìm hiểu cách mở rộng schema GraphQL (hiện đang trong quá trình phát triển).

Filtering blocks

Trường CustomPost.blocks chứa đối số filterBy với hai thuộc tính: includeexclude. Chúng ta có thể sử dụng chúng để lọc những khối nào được truy xuất, theo tên khối:

{
  post(by: { id: 1 }) {
    id
    blocks(
      filterBy: {
        include: [
          "core/heading",
          "core/gallery"
        ]
      }
    ) {
      name
      attributes
    }
  }
}

Điều này sẽ tạo ra:

{
  "data": {
    "post": {
      "blocks": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlocks": null
        }
      ]
    }
  }
}

Lưu ý rằng không phải tất cả các khối có kiểu core/heading đều được bao gồm: Những khối nằm lồng bên trong core/column đã bị loại trừ, vì không có cách nào để đến được chúng (vì các khối core/columnscore/column bản thân chúng cũng bị loại trừ).

Bất tiện của trường blocks

Trường blocks tạo ra sự bất tiện rằng, để truy xuất toàn bộ dữ liệu khối có trong custom post, bao gồm cả dữ liệu cho các khối bên trong, và các khối bên trong của chúng, v.v., chúng ta phải biết có bao nhiêu cấp độ lồng nhau của khối trong nội dung, và phản ánh thông tin này trong query GraphQL.

Hoặc, nếu chúng ta không biết, chúng ta phải soạn query với đủ cấp độ để chắc chắn rằng tất cả dữ liệu sẽ được lấy.

Ví dụ, query này truy xuất tới 7 cấp độ lồng nhau của khối bên trong:

{
  post(by: { id: 1 }) {
    blocks {
      ...BlockData
    }
  }
}
 
fragment BlockData on Block {
  name
  attributes
  innerBlocks {
    ...on Block {
      name
      attributes
      innerBlocks {
        ...on Block {
          name
          attributes
          innerBlocks {
            ...on Block {
              name
              attributes
              innerBlocks {
                ...on Block {
                  name
                  attributes
                  innerBlocks {
                    ...on Block {
                      name
                      attributes
                      innerBlocks {
                        ...on Block {
                          name
                          attributes
                          innerBlocks {
                            ...on Block {
                              name
                              attributes
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

blockDataItems

Để tránh sự bất tiện của cách trường blocks truy xuất tất cả dữ liệu (bao gồm cả cho các khối bên trong của nó, và các khối bên trong của chúng, v.v.), đó là lý do tồn tại trường CustomPost.blockDataItems.

Trường này, thay vì trả về [BlockUnion], trả về [JSONObject!]:

type CustomPost {
  blockDataItems: [JSONObject!]
}

Nói cách khác, thay vì theo cách GraphQL thông thường là các thực thể liên quan đến các thực thể và điều hướng qua chúng, mỗi thực thể Block ở cấp cao nhất đã tạo ra toàn bộ dữ liệu khối cho chính nó và tất cả các con của nó, trong một kết quả JSONObject duy nhất.

Đối tượng JSON chứa các thuộc tính cho khối (dưới các mục nameattributes) và cho các khối bên trong của nó (dưới mục innerBlocks), một cách đệ quy.

Ví dụ, query sau:

{
  post(by: { id: 1 }) {
    blockDataItems
  }
}

...sẽ tạo ra:

{
  "data": {
    "post": {
      "blockDataItems": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          }
        },
        {
          "name": "core/list",
          "attributes": {
            "ordered": false,
            "values": "<li>List item 1</li><li>List item 2</li><li>List item 3</li><li>List item 4</li>"
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          }
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  }
                }
              ]
            },
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/paragraph",
                  "attributes": {
                    "className": "layout-column-2",
                    "content": "Phosfluorescently morph intuitive relationships rather than customer directed human capital.",
                    "dropCap": false
                  }
                }
              ]
            }
          ]
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          }
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlocks": [
            {
              "name": "core/column",
              "attributes": {},
              "innerBlocks": [
                {
                  "name": "core/image",
                  "attributes": {
                    "id": 1701,
                    "className": "layout-column-1",
                    "url": "https://d.pr/i/fW6V3V+",
                    "alt": ""
                  }
                },
                {
                  "name": "core/columns",
                  "attributes": {
                    "isStackedOnMobile": true
                  },
                  "innerBlocks": [
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "33.33%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/heading",
                          "attributes": {
                            "fontSize": "large",
                            "content": "Life is so rich",
                            "level": 2
                          }
                        },
                        {
                          "name": "core/heading",
                          "attributes": {
                            "level": 3,
                            "content": "Life is so dynamic"
                          }
                        }
                      ]
                    },
                    {
                      "name": "core/column",
                      "attributes": {
                        "width": "66.66%"
                      },
                      "innerBlocks": [
                        {
                          "name": "core/paragraph",
                          "attributes": {
                            "content": "This rhyming poem is the spark that can reignite the fires within you. It challenges you to go out and live your life in the present moment as a \u201chero\u201d and leave your mark on this world.",
                            "dropCap": false
                          }
                        },
                        {
                          "name": "core/columns",
                          "attributes": {
                            "isStackedOnMobile": true
                          },
                          "innerBlocks": [
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 361,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/graphql-voyager-public-1024x622.jpg",
                                    "alt": ""
                                  }
                                }
                              ]
                            },
                            {
                              "name": "core/column",
                              "attributes": {}
                            },
                            {
                              "name": "core/column",
                              "attributes": {},
                              "innerBlocks": [
                                {
                                  "name": "core/image",
                                  "attributes": {
                                    "id": 362,
                                    "sizeSlug": "large",
                                    "linkDestination": "none",
                                    "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/namespaced-interactive-schema-1024x598.webp",
                                    "alt": ""
                                  }
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

Filtering block data items

Tương tự như blocks, blockDataItems cũng cho phép lọc những khối nào được truy xuất, thông qua đối số filterBy.

Query này:

{
  post(by: { id: 1 }) {
    id
    blockDataItems(
      filterBy: {
        include: [
          "core/heading"
        ]
      }
    )
  }
}

...sẽ tạo ra:

{
  "data": {
    "post": {
      "blockDataItems": [
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlocks": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlocks": null
        }
      ]
    }
  }
}

Lưu ý rằng, tương tự như blocks, không phải tất cả các khối có kiểu core/heading đều được bao gồm: Những khối nằm lồng bên trong core/column đã bị loại trừ, vì không có cách nào để đến được chúng (vì các khối core/columnscore/column bản thân chúng cũng bị loại trừ).

blockFlattenedDataItems

Cả hai trường blocksblockDataItems đều cho phép lọc những khối nào được truy xuất (thông qua đối số filterBy). Trong cả hai trường hợp, nếu một khối thỏa mãn điều kiện bao gồm, nhưng lại nằm lồng bên trong một khối không thỏa mãn, thì nó sẽ bị loại trừ.

Tuy nhiên, có những trường hợp khi chúng ta cần truy xuất tất cả các khối của một loại nhất định từ custom post, bất kể các khối đó nằm ở đâu trong phân cấp. Ví dụ, chúng ta có thể muốn bao gồm tất cả các khối kiểu core/image, để truy xuất tất cả hình ảnh có trong một bài đăng blog.

Để đáp ứng nhu cầu này, trường CustomPost.blockFlattenedDataItems được tạo ra. Không giống như các trường blocksblockDataItems, nó làm phẳng phân cấp khối thành một cấp độ duy nhất.

Query này:

{
  post(by: { id: 1 }) {
    blockFlattenedDataItems
  }
}

...sẽ tạo ra:

{
  "data": {
    "post": {
      "blockFlattenedDataItems": [
        {
          "name": "core/gallery",
          "attributes": {
            "linkTo": "none",
            "className": "alignnone",
            "images": [
              {
                "url": "https://d.pr/i/zd7Ehu+",
                "alt": "",
                "id": "1706"
              },
              {
                "url": "https://d.pr/i/jXLtzZ+",
                "alt": "",
                "id": "1705"
              }
            ],
            "ids": [],
            "shortCodeTransforms": [],
            "imageCrop": true,
            "fixedHeight": true,
            "sizeSlug": "large",
            "allowResize": false
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/list",
          "attributes": {
            "ordered": false,
            "values": "<li>List item 1</li><li>List item 2</li><li>List item 3</li><li>List item 4</li>"
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlockPositions": [
            5,
            7
          ],
          "parentBlockPosition": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 4,
          "innerBlockPositions": [
            6
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 1701,
            "className": "layout-column-1",
            "url": "https://d.pr/i/fW6V3V+",
            "alt": ""
          },
          "parentBlockPosition": 5,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 4,
          "innerBlockPositions": [
            8
          ]
        },
        {
          "name": "core/paragraph",
          "attributes": {
            "className": "layout-column-2",
            "content": "Phosfluorescently morph intuitive relationships rather than customer directed human capital.",
            "dropCap": false
          },
          "parentBlockPosition": 7,
          "innerBlockPositions": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          },
          "innerBlockPositions": null,
          "parentBlockPosition": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "innerBlockPositions": [
            11
          ],
          "parentBlockPosition": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 10,
          "innerBlockPositions": [
            12,
            13
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 1701,
            "className": "layout-column-1",
            "url": "https://d.pr/i/fW6V3V+",
            "alt": ""
          },
          "parentBlockPosition": 11,
          "innerBlockPositions": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "parentBlockPosition": 11,
          "innerBlockPositions": [
            14,
            17
          ]
        },
        {
          "name": "core/column",
          "attributes": {
            "width": "33.33%"
          },
          "parentBlockPosition": 13,
          "innerBlockPositions": [
            15,
            16
          ]
        },
        {
          "name": "core/heading",
          "attributes": {
            "fontSize": "large",
            "content": "Life is so rich",
            "level": 2
          },
          "parentBlockPosition": 14,
          "innerBlockPositions": null
        },
        {
          "name": "core/heading",
          "attributes": {
            "level": 3,
            "content": "Life is so dynamic"
          },
          "parentBlockPosition": 14,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {
            "width": "66.66%"
          },
          "parentBlockPosition": 13,
          "innerBlockPositions": [
            18,
            19
          ]
        },
        {
          "name": "core/paragraph",
          "attributes": {
            "content": "This rhyming poem is the spark that can reignite the fires within you. It challenges you to go out and live your life in the present moment as a \u201chero\u201d and leave your mark on this world.",
            "dropCap": false
          },
          "parentBlockPosition": 17,
          "innerBlockPositions": null
        },
        {
          "name": "core/columns",
          "attributes": {
            "isStackedOnMobile": true
          },
          "parentBlockPosition": 17,
          "innerBlockPositions": [
            20,
            22,
            23
          ]
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 19,
          "innerBlockPositions": [
            21
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 361,
            "sizeSlug": "large",
            "linkDestination": "none",
            "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/graphql-voyager-public-1024x622.jpg",
            "alt": ""
          },
          "parentBlockPosition": 20,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 19,
          "innerBlockPositions": null
        },
        {
          "name": "core/column",
          "attributes": {},
          "parentBlockPosition": 19,
          "innerBlockPositions": [
            24
          ]
        },
        {
          "name": "core/image",
          "attributes": {
            "id": 362,
            "sizeSlug": "large",
            "linkDestination": "none",
            "url": "https://gato-graphql.lndo.site/wp-content/uploads/2022/05/namespaced-interactive-schema-1024x598.webp",
            "alt": ""
          },
          "parentBlockPosition": 23,
          "innerBlockPositions": null
        }
      ]
    }
  }
}

Lưu ý cách thuộc tính innerBlocks đã biến mất, vì các khối không còn được lồng nhau nữa. Thay vào đó, phản hồi bao gồm hai thuộc tính khác (cho phép chúng ta tái tạo phân cấp khối):

  • parentBlockPosition: Vị trí của khối cha của khối trong mảng trả về, hoặc null nếu đó là khối cấp cao nhất
  • innerBlockPositions: Một mảng với các vị trí của các khối bên trong của khối trong mảng trả về

Filtering the flattened block data items

Bây giờ phân cấp khối đã được làm phẳng, việc lọc theo core/heading sẽ tạo ra tất cả các khối này (ngay cả khi bất kỳ khối nào trong số chúng ban đầu nằm lồng bên trong một khối đã bị loại trừ).

Query này:

{
  post(by: { id: 1 }) {
    id
    blockFlattenedDataItems(
      filterBy: {
        include: [
          "core/heading"
        ]
      }
    )
  }
}

...sẽ tạo ra:

{
  "data": {
    "post": {
      "blockFlattenedDataItems": [
        {
          "name": "core/heading",
          "attributes": {
            "content": "List Block",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "className": "has-top-margin",
            "content": "Columns Block",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "content": "Columns inside Columns (nested inner blocks)",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "fontSize": "large",
            "content": "Life is so rich",
            "level": 2
          }
        },
        {
          "name": "core/heading",
          "attributes": {
            "level": 3,
            "content": "Life is so dynamic"
          }
        }
      ]
    }
  }
}

Lưu ý rằng hai thuộc tính bổ sung, parentBlockPositioninnerBlockPositions, bị xóa khi lọc, vì chúng không còn có ý nghĩa nữa.