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ư Post và Page):
blocksblockDataItemsblockFlattenedDataItems
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 = GenericBlockBlock fields
Giao diện Block (và do đó, kiểu GeneralBlock) chứa các trường sau:
nametruy xuất tên của khối:"core/paragraph","core/heading""core/image", v.v.attributestruy xuất một đối tượng JSON chứa tất cả các thuộc tính của khối.innerBlockstruy 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.contentSourcetruy 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?:

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: include và exclude. 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/columns và core/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 name và attributes) 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/columns và core/column bản thân chúng cũng bị loại trừ).
blockFlattenedDataItems
Cả hai trường blocks và blockDataItems đề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 blocks và blockDataItems, 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ặcnullnếu đó là khối cấp cao nhấtinnerBlockPositions: 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, parentBlockPosition và innerBlockPositions, bị xóa khi lọc, vì chúng không còn có ý nghĩa nữa.