Travailler avec
Travailler avecBlocs (Gutenberg)

Blocs (Gutenberg)

Voici comment récupérer les données des blocs Gutenberg.

Le schéma GraphQL comporte les champs suivants ajoutés à tous les types CustomPost (tels que Post et Page) :

  • blocks
  • blockDataItems
  • blockFlattenedDataItems

Ces champs ne sont pas activés si le plugin Classic Editor est actif.

blocks

Le champ CustomPost.blocks: [BlockUnion!] récupère la liste de tous les blocs contenus dans le custom post.

blocks retourne une liste des types Block qui ont été mappés au schéma GraphQL. Ces types Block font tous partie du type BlockUnion et implémentent l'interface Block.

Le plugin implémente un type Block, GenericBlock, qui est déjà suffisant pour récupérer les données de n'importe quel bloc (via le champ attributes: JSONObject).

Cette requête :

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

...produira cette réponse :

{
  "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 “hero” 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
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

Le schéma GraphQL pour les types Block ressemble à ceci :

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

Champs de Block

L'interface Block (et, par conséquent, le type GeneralBlock) contient les champs suivants :

  • name récupère le nom du bloc : "core/paragraph", "core/heading", "core/image", etc.
  • attributes récupère un objet JSON contenant tous les attributs du bloc.
  • innerBlocks récupère [BlockUnion!], ce qui nous permet de l'interroger pour naviguer dans la hiérarchie des blocs contenant des blocs internes, et de récupérer les données pour tous, pour autant de niveaux que nous avons dans notre contenu.
  • contentSource récupère le code source HTML (Gutenberg) du bloc, y compris les délimiteurs de commentaire qui contiennent les attributs. Cependant, ce champ ne récupère pas exactement les mêmes données que celles stockées en base de données (voir #2346), donc utilisez ce champ avec précaution.

Récupérer GeneralBlock directement (au lieu de BlockUnion)

Comme il n'existe actuellement qu'un seul type Block mappant les blocs — GeneralBlock — il est logique que CustomPost.blocks (et aussi Block.innerBlocks) récupère ce type directement, au lieu du type BlockUnion.

Nous pouvons le faire dans la page Settings sous l'onglet Blocks, en cochant l'option Use single type instead of union type? :

Configurer pour récupérer `GeneralBlock` directement au lieu de `BlockUnion`
Configurer pour récupérer `GeneralBlock` directement au lieu de `BlockUnion`

Alors, la requête GraphQL se simplifie :

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

Notez que conserver le type de réponse comme BlockUnion est utile pour la compatibilité future : si nous décidons un jour d'ajouter des types spécifiques à des blocs dans le schéma (voir la section ci-dessous), il n'y aura pas de changements incompatibles.

Mapper des types spécifiques à des blocs

Le type JSONObject (tel que récupéré par Block.attributes) n'est pas strictement typé : ses propriétés peuvent avoir n'importe quel type et cardinalité (String, Int, [Boolean!], etc.), nous devons donc connaître ces informations pour chaque bloc et traiter chaque cas côté client.

Si nous avons besoin d'un typage strict, nous devons étendre le schéma GraphQL via du code PHP, en ajoutant des types spécifiques à des blocs qui mappent les attributs spécifiques d'un bloc sous forme de champs, et les rendre partie de BlockUnion.

Par exemple, nous pouvons ajouter le type CoreParagraphBlock qui mappe le bloc core/paragraph, avec un champ content de type String.

Consultez la documentation dans GatoGraphQL/GatoGraphQL pour apprendre comment étendre le schéma GraphQL (actuellement en cours de développement).

Filtrer les blocs

Le champ CustomPost.blocks contient l'argument filterBy avec deux propriétés : include et exclude. Nous pouvons les utiliser pour filtrer les blocs récupérés, par nom de bloc :

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

Cela produira :

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

Notez que tous les blocs de type core/heading n'ont pas été inclus : ceux imbriqués sous core/column ont été exclus, car il n'y a aucun moyen de les atteindre (puisque les blocs core/columns et core/column sont eux-mêmes exclus).

Inconvénients du champ blocks

Le champ blocks présente l'inconvénient que, pour récupérer toutes les données de bloc contenues dans le custom post, y compris les données des blocs internes, et leurs propres blocs internes, et ainsi de suite, nous devons connaître le nombre de niveaux de blocs imbriqués dans le contenu, et refléter cette information dans la requête GraphQL.

Ou, si nous ne le savons pas, nous devons composer la requête avec suffisamment de niveaux pour être certains que toutes les données seront récupérées.

Par exemple, cette requête récupère jusqu'à 7 niveaux d'imbrication de blocs internes :

{
  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

C'est pour éviter l'inconvénient lié à la façon dont le champ blocks récupère toutes les données (y compris celles de ses blocs internes, et des blocs internes de ceux-ci, et ainsi de suite) qu'existe le champ CustomPost.blockDataItems.

Ce champ, au lieu de retourner [BlockUnion], retourne [JSONObject!] :

type CustomPost {
  blockDataItems: [JSONObject!]
}

En d'autres termes, au lieu de suivre la manière typique de GraphQL où les entités se relient à des entités et nous naviguons à travers elles, chaque entité Block au niveau supérieur produit déjà toutes les données de bloc pour elle-même et tous ses enfants, dans un seul résultat JSONObject.

L'objet JSON contient les propriétés du bloc (sous les entrées name et attributes) et celles de ses blocs internes (sous l'entrée innerBlocks), de manière récursive.

Par exemple, la requête suivante :

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

...produira :

{
  "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 “hero” 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": ""
                                  }
                                }
                              ]
                            }
                          ]
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }
  }
}

Filtrer les éléments de données de bloc

De même que blocks, blockDataItems permet également de filtrer les blocs récupérés, via l'argument filterBy.

Cette requête :

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

...produira :

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

Notez que, de même qu'avec blocks, tous les blocs de type core/heading n'ont pas été inclus : ceux imbriqués sous core/column ont été exclus, car il n'y a aucun moyen de les atteindre (puisque les blocs core/columns et core/column sont eux-mêmes exclus).

blockFlattenedDataItems

Les deux champs blocks et blockDataItems permettent de filtrer les blocs récupérés (via l'argument filterBy). Dans les deux cas, si un bloc satisfait la condition d'inclusion, mais est imbriqué dans un bloc qui ne la satisfait pas, il sera exclu.

Il y a des occasions, cependant, où nous avons besoin de récupérer tous les blocs d'un certain type depuis le custom post, indépendamment de l'endroit où ces blocs se trouvent dans la hiérarchie. Par exemple, nous pourrions vouloir inclure tous les blocs de type core/image, pour récupérer toutes les images incluses dans un article de blog.

C'est pour répondre à ce besoin qu'existe le champ CustomPost.blockFlattenedDataItems. Contrairement aux champs blocks et blockDataItems, il aplatit la hiérarchie des blocs en un seul niveau.

Cette requête :

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

...produira :

{
  "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 “hero” 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
        }
      ]
    }
  }
}

Notez comment l'attribut innerBlocks a disparu, car les blocs ne sont plus imbriqués. À sa place, la réponse inclut deux autres attributs (qui nous permettent de reconstruire la hiérarchie des blocs) :

  • parentBlockPosition : la position du bloc parent dans le tableau retourné, ou null s'il s'agit d'un bloc de niveau supérieur
  • innerBlockPositions : un tableau avec les positions des blocs internes du bloc dans le tableau retourné

Filtrer les éléments de données de bloc aplatis

Maintenant que la hiérarchie des blocs a été aplatie, filtrer par core/heading produira tous ces blocs (même si l'un d'eux est initialement imbriqué sous un bloc qui a été exclu).

Cette requête :

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

...produira :

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

Notez que les deux attributs supplémentaires, parentBlockPosition et innerBlockPositions, sont supprimés lors du filtrage, car ils n'ont plus de sens.