Leonardo Losoviz
Creator of Gato GraphQL, and contributor to several online magazines, including Smashing Magazine and CSS Tricks.
class="text-center" doesn't make sense for an audio-based medium
<p class="align-center">Hello world!</p>
{
type: "paragraph",
content: "Hello world!",
placement: "center"
}
Versatile
The DB enables the creation of distinct content models through the use of meta fields
Powerful
It shines as a CMS, featuring a plugin ecosystem to add new functionalities
Widespread
Most people working on the web know about/know how to use it, including non-techies
It has a block-based editor, Gutenberg
Blocks enable to easily export the post content metadata...
A blob is a single unit of information stored all together in the database
<p>Look at this wonderful tango:</p>
<figure>
<iframe width="951" height="535" src="https://www.youtube.com/embed/sxm3Xyutc1s" frameborder="0" allow="autoplay;" allowfullscreen></iframe>
<figcaption>An exquisite tango performance</figcaption>
</figure>
Before version 5.0, WordPress used blobs
A block conveys its own content and properties as metadata, customized to its type (paragraph/video/etc)
{
[
type: "paragraph",
content: "Look at this wonderful tango:"
],
[
type: "embed",
provider: "Youtube",
url: "https://www.youtube.com/embed/sxm3Xyutc1s",
width: 951,
height: 535,
frameborder: 0,
allowfullscreen: true,
allow: "accelerometer; autoplay;",
caption: "An exquisite tango performance"
]
}
We can easily use any piece of data on its own, and adapt it for the specific medium where it must be displayed
Since version 5.0, WordPress uses blocks (via Gutenberg)
<!-- wp:paragraph -->
<p>Look at this wonderful tango:</p>
<!-- /wp:paragraph -->
<!-- wp:core-embed/youtube {"url":"https://www.youtube.com/embed/sxm3Xyutc1s","type":"rich","providerNameSlug":"embed-handler","className":"wp-embed-aspect-16-9 wp-has-aspect-ratio"} -->
<figure class="wp-block-embed-youtube wp-block-embed is-type-rich is-provider-embed-handler wp-embed-aspect-16-9 wp-has-aspect-ratio">
<div class="wp-block-embed__wrapper"> https://www.youtube.com/embed/sxm3Xyutc1s </div>
<figcaption>An exquisite tango performance</figcaption>
</figure>
<!-- /wp:core-embed/youtube -->
We can make several observations...
<!-- wp:paragraph -->
<p>Look at this wonderful tango:</p>
<!-- /wp:paragraph -->
<!-- wp:core-embed/youtube {"url":"https://www.youtube.com/embed/sxm3Xyutc1s","type":"rich","providerNameSlug":"embed-handler","className":"wp-embed-aspect-16-9 wp-has-aspect-ratio"} -->
<figure class="wp-block-embed-youtube wp-block-embed is-type-rich is-provider-embed-handler wp-embed-aspect-16-9 wp-has-aspect-ratio">
<div class="wp-block-embed__wrapper"> https://www.youtube.com/embed/sxm3Xyutc1s </div>
<figcaption>An exquisite tango performance</figcaption>
</figure>
<!-- /wp:core-embed/youtube -->
1. Blocks are saved all together in the same database entry
<!-- wp:paragraph -->
<p>Look at this wonderful tango:</p>
<!-- /wp:paragraph -->
<!-- wp:core-embed/youtube {"url":"https://www.youtube.com/embed/sxm3Xyutc1s","type":"rich","providerNameSlug":"embed-handler","className":"wp-embed-aspect-16-9 wp-has-aspect-ratio"} -->
<figure class="wp-block-embed-youtube wp-block-embed is-type-rich is-provider-embed-handler wp-embed-aspect-16-9 wp-has-aspect-ratio">
<div class="wp-block-embed__wrapper"> https://www.youtube.com/embed/sxm3Xyutc1s </div>
<figcaption>An exquisite tango performance</figcaption>
</figure>
<!-- /wp:core-embed/youtube -->
2. Block type/attributes are stored using HTML comments
<!-- wp:paragraph -->
<p>Look at this wonderful tango:</p>
<!-- /wp:paragraph -->
<!-- wp:core-embed/youtube {"url":"https://www.youtube.com/embed/sxm3Xyutc1s","type":"rich","providerNameSlug":"embed-handler","className":"wp-embed-aspect-16-9 wp-has-aspect-ratio"} -->
<figure class="wp-block-embed-youtube wp-block-embed is-type-rich is-provider-embed-handler wp-embed-aspect-16-9 wp-has-aspect-ratio">
<div class="wp-block-embed__wrapper"> https://www.youtube.com/embed/sxm3Xyutc1s </div>
<figcaption>An exquisite tango performance</figcaption>
</figure>
<!-- /wp:core-embed/youtube -->
3. Blocks contain HTML
<!-- wp:paragraph -->
<p>Look at this wonderful tango:</p>
<!-- /wp:paragraph -->
<!-- wp:core-embed/youtube {"url":"https://www.youtube.com/embed/sxm3Xyutc1s","type":"rich","providerNameSlug":"embed-handler","className":"wp-embed-aspect-16-9 wp-has-aspect-ratio"} -->
<figure class="wp-block-embed-youtube wp-block-embed is-type-rich is-provider-embed-handler wp-embed-aspect-16-9 wp-has-aspect-ratio">
<div class="wp-block-embed__wrapper"> https://www.youtube.com/embed/sxm3Xyutc1s </div>
<figcaption>An exquisite tango performance</figcaption>
</figure>
<!-- /wp:core-embed/youtube -->
4. The video's caption is stored in HTML, not as an attribute
function extract_caption($content)
{
$matches = [];
preg_match('/<figcaption>(.*?)<\/figcaption>/', $content, $matches);
if ($caption = $matches[1]) {
return strip_tags($caption, '<strong><em>');
}
return null;
}
WordPress plugin "Block Metadata" released with all the COPE implementation code
The procedure requires the following steps:
[
// Simple block
{
"blockName": "core/image",
"attrs": {
"id": 70,
"sizeSlug": "large"
},
"innerBlocks": [],
"innerHTML": "\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/sandwich-1024x614.jpg\" alt=\"\" class=\"wp-image-70\"/><figcaption>This is a normal block</figcaption></figure>\n",
"innerContent": [
"\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/sandwich-1024x614.jpg\" alt=\"\" class=\"wp-image-70\"/><figcaption>This is a normal block</figcaption></figure>\n"
]
},
// Empty block divider
{
"blockName": null,
"attrs": [],
"innerBlocks": [],
"innerHTML": "\n\n",
"innerContent": [
"\n\n"
]
},
// Reference to reusable block
{
"blockName": "core/block",
"attrs": {
"ref": 218
},
"innerBlocks": [],
"innerHTML": "",
"innerContent": []
},
// Empty block divider
{
"blockName": null,
"attrs": [],
"innerBlocks": [],
"innerHTML": "\n\n",
"innerContent": [
"\n\n"
]
},
// Nested block
{
"blockName": "core/columns",
"attrs": [],
// Contained nested blocks
"innerBlocks": [
{
"blockName": "core/column",
"attrs": [],
// Contained nested blocks
"innerBlocks": [
{
"blockName": "core/image",
"attrs": {
"id": 69,
"sizeSlug": "large"
},
"innerBlocks": [],
"innerHTML": "\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/espresso-1024x614.jpg\" alt=\"\" class=\"wp-image-69\"/></figure>\n",
"innerContent": [
"\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/espresso-1024x614.jpg\" alt=\"\" class=\"wp-image-69\"/></figure>\n"
]
}
],
"innerHTML": "\n<div class=\"wp-block-column\"></div>\n",
"innerContent": [
"\n<div class=\"wp-block-column\">",
null,
"</div>\n"
]
},
{
"blockName": "core/column",
"attrs": [],
// Contained nested blocks
"innerBlocks": [
{
"blockName": "core/paragraph",
"attrs": [],
"innerBlocks": [],
"innerHTML": "\n<p>This is how I wake up every morning</p>\n",
"innerContent": [
"\n<p>This is how I wake up every morning</p>\n"
]
}
],
"innerHTML": "\n<div class=\"wp-block-column\"></div>\n",
"innerContent": [
"\n<div class=\"wp-block-column\">",
null,
"</div>\n"
]
}
],
"innerHTML": "\n<div class=\"wp-block-columns\">\n\n</div>\n",
"innerContent": [
"\n<div class=\"wp-block-columns\">",
null,
"\n\n",
null,
"</div>\n"
]
},
// Empty block divider
{
"blockName": null,
"attrs": [],
"innerBlocks": [],
"innerHTML": "\n\n",
"innerContent": [
"\n\n"
]
},
// Block group
{
"blockName": "core/group",
"attrs": [],
// Contained grouped blocks
"innerBlocks": [
{
"blockName": "core/image",
"attrs": {
"id": 71,
"sizeSlug": "large"
},
"innerBlocks": [],
"innerHTML": "\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/coffee-1024x614.jpg\" alt=\"\" class=\"wp-image-71\"/><figcaption>First element of the group</figcaption></figure>\n",
"innerContent": [
"\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/coffee-1024x614.jpg\" alt=\"\" class=\"wp-image-71\"/><figcaption>First element of the group</figcaption></figure>\n"
]
},
{
"blockName": "core/paragraph",
"attrs": [],
"innerBlocks": [],
"innerHTML": "\n<p>Second element of the group</p>\n",
"innerContent": [
"\n<p>Second element of the group</p>\n"
]
}
],
"innerHTML": "\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container\">\n\n</div></div>\n",
"innerContent": [
"\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container\">",
null,
"\n\n",
null,
"</div></div>\n"
]
}
]
The JSON object returned by function parse_blocks($content) has missing data (for referenced reusable blocks), and data under multiple levels (for nested and group blocks)
/**
* Export all (Gutenberg) blocks' data from a WordPress post
*/
function get_block_data($content, $remove_divider_block = true)
{
// Parse the blocks, and convert them into a single-level array
$ret = [];
$blocks = parse_blocks($content);
recursively_add_blocks($ret, $blocks);
// Maybe remove blocks without name
if ($remove_divider_block) {
$ret = remove_blocks_without_name($ret);
}
// Remove 'innerBlocks' properties for all blocks (since that code was copied to the first level, it is currently duplicated)
foreach ($ret as &$block) {
unset($block['innerBlocks']);
}
return $ret;
}
/**
* Remove the blocks without name, such as the empty block divider
*/
function remove_blocks_without_name($blocks)
{
return array_values(array_filter(
$blocks,
function($block) {
return $block['blockName'];
}
));
}
/**
* Add block data (including global and nested blocks) into the first level of the array
*/
function recursively_add_blocks(&$ret, $blocks)
{
foreach ($blocks as $block) {
// Global block: add the referenced block instead of this one
if ($block['attrs']['ref']) {
$ret = array_merge(
$ret,
recursively_render_block_core_block($block['attrs'])
);
}
// Normal block: add it directly
else {
$ret[] = $block;
}
// If it contains nested or grouped blocks, add them too
if ($block['innerBlocks']) {
recursively_add_blocks($ret, $block['innerBlocks']);
}
}
}
/**
* Function based on `render_block_core_block`
*/
function recursively_render_block_core_block($attributes)
{
if (empty($attributes['ref'])) {
return [];
}
$reusable_block = get_post($attributes['ref']);
if (!$reusable_block || 'wp_block' !== $reusable_block->post_type) {
return [];
}
if ('publish' !== $reusable_block->post_status || ! empty($reusable_block->post_password)) {
return [];
}
return get_block_data($reusable_block->post_content);
}
We must fetch the data for the reusable/nested/grouped blocks, and add it on the first level of the JSON object
[
{
"blockName": "core/image",
"attrs": {
"id": 70,
"sizeSlug": "large"
},
"innerHTML": "\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/sandwich-1024x614.jpg\" alt=\"\" class=\"wp-image-70\"/><figcaption>This is a normal block</figcaption></figure>\n",
"innerContent": [
"\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/sandwich-1024x614.jpg\" alt=\"\" class=\"wp-image-70\"/><figcaption>This is a normal block</figcaption></figure>\n"
]
},
{
"blockName": "core/paragraph",
"attrs": [],
"innerHTML": "\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>\n",
"innerContent": [
"\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>\n"
]
},
{
"blockName": "core/columns",
"attrs": [],
"innerHTML": "\n<div class=\"wp-block-columns\">\n\n</div>\n",
"innerContent": [
"\n<div class=\"wp-block-columns\">",
null,
"\n\n",
null,
"</div>\n"
]
},
{
"blockName": "core/column",
"attrs": [],
"innerHTML": "\n<div class=\"wp-block-column\"></div>\n",
"innerContent": [
"\n<div class=\"wp-block-column\">",
null,
"</div>\n"
]
},
{
"blockName": "core/image",
"attrs": {
"id": 69,
"sizeSlug": "large"
},
"innerHTML": "\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/espresso-1024x614.jpg\" alt=\"\" class=\"wp-image-69\"/></figure>\n",
"innerContent": [
"\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/espresso-1024x614.jpg\" alt=\"\" class=\"wp-image-69\"/></figure>\n"
]
},
{
"blockName": "core/column",
"attrs": [],
"innerHTML": "\n<div class=\"wp-block-column\"></div>\n",
"innerContent": [
"\n<div class=\"wp-block-column\">",
null,
"</div>\n"
]
},
{
"blockName": "core/paragraph",
"attrs": [],
"innerHTML": "\n<p>This is how I wake up every morning</p>\n",
"innerContent": [
"\n<p>This is how I wake up every morning</p>\n"
]
},
{
"blockName": "core/group",
"attrs": [],
"innerHTML": "\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container\">\n\n</div></div>\n",
"innerContent": [
"\n<div class=\"wp-block-group\"><div class=\"wp-block-group__inner-container\">",
null,
"\n\n",
null,
"</div></div>\n"
]
},
{
"blockName": "core/image",
"attrs": {
"id": 71,
"sizeSlug": "large"
},
"innerHTML": "\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/coffee-1024x614.jpg\" alt=\"\" class=\"wp-image-71\"/><figcaption>First element of the group</figcaption></figure>\n",
"innerContent": [
"\n<figure class=\"wp-block-image size-large\"><img src=\"http://localhost/wp-content/uploads/2017/12/coffee-1024x614.jpg\" alt=\"\" class=\"wp-image-71\"/><figcaption>First element of the group</figcaption></figure>\n"
]
},
{
"blockName": "core/paragraph",
"attrs": [],
"innerHTML": "\n<p>Second element of the group</p>\n",
"innerContent": [
"\n<p>Second element of the group</p>\n"
]
}
]
The resulting JSON object contains all data for all blocks in the post, and is easy to iterate
// Define REST endpoint to visualize a post's block data
add_action('rest_api_init', function () {
register_rest_route('block-metadata/v1', 'data/(?P<post_id>\d+)', [
'methods' => 'GET',
'callback' => 'get_post_blocks'
]);
});
function get_post_blocks($request)
{
$post = get_post($request['post_id']);
if (!$post) {
return new WP_Error('empty_post', 'There is no post with this ID', ['status' => 404]);
}
$block_data = get_block_data($post->post_content);
$response = new WP_REST_Response($block_data);
$response->set_status(200);
return $response;
}
We can create an endpoint to access this data through the REST API: /wp-json/block-metadata/v1/data/{POST_ID}
Let's check the results:
Not all block types can work with COPE
The following core blocks can be processed:
To extract the metadata, we create function get_block_metadata($block_data). Based on the block type, it defines what attributes to extract and how to do it
/**
* Process all (Gutenberg) blocks' metadata into a medium-agnostic format from a WordPress post
*/
function get_block_metadata($block_data)
{
$ret = [];
foreach ($block_data as $block) {
$blockMeta = null;
switch ($block['blockName']) {
case 'core/paragraph':
$blockMeta = [
'content' => trim(strip_html_tags($block['innerHTML'])),
];
break;
case 'core/image':
$blockMeta = [];
// If inserting the image from the Media Manager, it has an ID
if ($block['attrs']['id'] && $img = wp_get_attachment_image_src($block['attrs']['id'], $block['attrs']['sizeSlug'])) {
$blockMeta['img'] = [
'src' => $img[0],
'width' => $img[1],
'height' => $img[2],
];
}
elseif ($src = extract_image_src($block['innerHTML'])) {
$blockMeta['src'] = $src;
}
if ($caption = extract_caption($block['innerHTML'])) {
$blockMeta['caption'] = $caption;
}
if ($linkDestination = $block['attrs']['linkDestination']) {
$blockMeta['linkDestination'] = $linkDestination;
if ($link = extract_link($block['innerHTML'])) {
$blockMeta['link'] = $link;
}
}
if ($align = $block['attrs']['align']) {
$blockMeta['align'] = $align;
}
break;
case 'core-embed/youtube':
$blockMeta = [
'url' => $block['attrs']['url'],
];
if ($caption = extract_caption($block['innerHTML'])) {
$blockMeta['caption'] = $caption;
}
break;
case 'core/heading':
$matches = [];
preg_match('/<h([1-6])>(.*?)<\/h([1-6])>/', $block['innerHTML'], $matches);
$sizes = [
null,
'xxl',
'xl',
'l',
'm',
'sm',
'xs',
];
$blockMeta = [
'size' => $sizes[$matches[1]],
'heading' => $matches[2],
];
break;
case 'core/gallery':
$imgs = [];
foreach ($block['attrs']['ids'] as $img_id) {
$img = wp_get_attachment_image_src($img_id, 'full');
$imgs[] = [
'src' => $img[0],
'width' => $img[1],
'height' => $img[2],
];
}
$blockMeta = [
'imgs' => $imgs,
];
break;
case 'core/list':
$matches = [];
preg_match_all('/<li>(.*?)<\/li>/', $block['innerHTML'], $matches);
if ($items = $matches[1]) {
$blockMeta = [
'items' => array_map('strip_html_tags', $items),
];
}
break;
case 'core/audio':
$blockMeta = [
'src' => wp_get_attachment_url($block['attrs']['id']),
];
break;
case 'core/file':
$href = $block['attrs']['href'];
$matches = [];
preg_match('/<a href="'.str_replace('/', '\/', $href).'">(.*?)<\/a>/', $block['innerHTML'], $matches);
$blockMeta = [
'href' => $href,
'text' => strip_html_tags($matches[1]),
];
break;
case 'core/video':
$matches = [];
preg_match('/<video (autoplay )?(controls )?(loop )?(muted )?(poster="(.*?)" )?src="(.*?)"( playsinline)?><\/video>/', $block['innerHTML'], $matches);
$blockMeta = [
'src' => $matches[7],
];
if ($poster = $matches[6]) {
$blockMeta['poster'] = $poster;
}
// Video settings
$settings = [];
if ($matches[1]) {
$settings[] = 'autoplay';
}
if ($matches[2]) {
$settings[] = 'controls';
}
if ($matches[3]) {
$settings[] = 'loop';
}
if ($matches[4]) {
$settings[] = 'muted';
}
if ($matches[8]) {
$settings[] = 'playsinline';
}
if ($settings) {
$blockMeta['settings'] = $settings;
}
if ($caption = extract_caption($block['innerHTML'])) {
$blockMeta['caption'] = $caption;
}
break;
case 'core/code':
$matches = [];
preg_match('/<code>(.*?)<\/code>/is', $block['innerHTML'], $matches);
$blockMeta = [
'code' => $matches[1],
];
break;
case 'core/preformatted':
$matches = [];
preg_match('/<pre class="wp-block-preformatted">(.*?)<\/pre>/is', $block['innerHTML'], $matches);
$blockMeta = [
'text' => strip_html_tags($matches[1]),
];
break;
case 'core/quote':
case 'core/pullquote':
$matches = [];
$regexes = [
'core/quote' => '/<blockquote class=\"wp-block-quote\">(.*?)<\/blockquote>/',
'core/pullquote' => '/<figure class=\"wp-block-pullquote\"><blockquote>(.*?)<\/blockquote><\/figure>/',
];
preg_match($regexes[$block['blockName']], $block['innerHTML'], $matches);
if ($quoteHTML = $matches[1]) {
preg_match_all('/<p>(.*?)<\/p>/', $quoteHTML, $matches);
$blockMeta = [
'quote' => strip_html_tags(implode('\n', $matches[1])),
];
preg_match('/<cite>(.*?)<\/cite>/', $quoteHTML, $matches);
if ($cite = $matches[1]) {
$blockMeta['cite'] = strip_html_tags($cite);
}
}
break;
case 'core/verse':
$matches = [];
preg_match('/<pre class="wp-block-verse">(.*?)<\/pre>/is', $block['innerHTML'], $matches);
$blockMeta = [
'text' => strip_html_tags($matches[1]),
];
break;
}
if ($blockMeta) {
$ret[] = [
'blockName' => $block['blockName'],
'meta' => $blockMeta,
];
}
}
return $ret;
}
function strip_html_tags($content)
{
return strip_tags($content, '<strong><em>');
}
function extract_caption($innerHTML)
{
$matches = [];
preg_match('/<figcaption>(.*?)<\/figcaption>/', $innerHTML, $matches);
if ($caption = $matches[1]) {
return strip_html_tags($caption);
}
return null;
}
function extract_link($innerHTML)
{
$matches = [];
preg_match('/<a href="(.*?)">(.*?)<\/a>/', $innerHTML, $matches);
if ($link = $matches[1]) {
return $link;
}
return null;
}
function extract_image_src($innerHTML)
{
$matches = [];
preg_match('/<img src="(.*?)"/', $innerHTML, $matches);
if ($src = $matches[1]) {
return $src;
}
return null;
}
Let's see how to export the data through each of them
Endpoint: /wp-json/block-metadata/v1/metadata/{POST_ID}
// Define REST endpoints to export the blocks' metadata for a specific post
add_action('rest_api_init', function () {
register_rest_route('block-metadata/v1', 'metadata/(?P<post_id>\d+)', [
'methods' => 'GET',
'callback' => 'get_post_block_meta'
]);
});
function get_post_block_meta($request)
{
$post = get_post($request['post_id']);
if (!$post) {
return new WP_Error('empty_post', 'There is no post with this ID', ['status' => 404]);
}
$block_data = get_block_data($post->post_content);
$block_metadata = get_block_metadata($block_data);
$response = new WP_REST_Response($block_metadata);
$response->set_status(200);
return $response;
}
Let's check the results:
Field: "jsonencoded_block_metadata"
/**
* Define WPGraphQL field "jsonencoded_block_metadata"
*/
add_action('graphql_register_types', function() {
register_graphql_field(
'Post',
'jsonencoded_block_metadata',
[
'type' => 'String',
'description' => __('Post blocks encoded as JSON', 'wp-graphql'),
'resolve' => function($post) {
$post = get_post($post->ID);
$block_data = get_block_data($post->post_content);
$block_metadata = get_block_metadata($block_data);
return json_encode($block_metadata);
}
]
);
});
Endpoint: /posts/api/?query=blockMetadata
class FieldValueResolverUnit extends \PoP\Engine\AbstractDBDataFieldValueResolverUnit
{
public static function getClassesToAttachTo()
{
return array(\PoP\Posts\FieldValueResolver_Posts::class);
}
public function getValue($fieldValueResolver, $resultitem, string $fieldName, array $fieldAtts = [])
{
$post = $resultitem;
switch ($fieldName) {
case 'blockMetadata':
$block_data = get_block_data($post->post_content);
$block_metadata = get_block_metadata($block_data);
// Filter by blockName
if ($blockName = $fieldAtts['blockName']) {
$block_metadata = array_filter(
$block_metadata,
function($block) use($blockName) {
return $block['blockName'] == $blockName;
}
);
}
return $block_metadata;
}
return parent::getValue($fieldValueResolver, $resultitem, $fieldName, $fieldAtts);
}
}
Let's check the results:
Code available under repo:
[
{
"blockName": "core/paragraph",
"meta": {
"content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Dolor sed viverra ipsum nunc aliquet bibendum enim. In massa tempor nec feugiat. Nunc aliquet bibendum enim facilisis gravida. Nisl nunc mi ipsum faucibus vitae aliquet nec ullamcorper. Amet luctus venenatis lectus magna fringilla. Volutpat maecenas volutpat blandit aliquam etiam erat velit scelerisque in. Egestas egestas fringilla phasellus faucibus scelerisque eleifend. Sagittis orci a scelerisque purus semper eget duis. Nulla pharetra diam sit amet nisl suscipit. Sed adipiscing diam donec adipiscing tristique risus nec feugiat in. Fusce ut placerat orci nulla. Pharetra vel turpis nunc eget lorem dolor. Tristique senectus et netus et malesuada."
}
},
{
"blockName": "core/image",
"meta": {
"src": "https://ps.w.org/gutenberg/assets/banner-1544x500.jpg"
}
},
{
"blockName": "core/paragraph",
"meta": {
"content": "<em>Etiam tempor orci eu lobortis elementum nibh tellus molestie. Neque egestas congue quisque egestas. Egestas integer eget aliquet nibh praesent tristique. Vulputate mi sit amet mauris. Sodales neque sodales ut etiam sit. Dignissim suspendisse in est ante in. Volutpat commodo sed egestas egestas. Felis donec et odio pellentesque diam. Pharetra vel turpis nunc eget lorem dolor sed viverra. Porta nibh venenatis cras sed felis eget. Aliquam ultrices sagittis orci a. Dignissim diam quis enim lobortis. Aliquet porttitor lacus luctus accumsan. Dignissim convallis aenean et tortor at risus viverra adipiscing at.</em>"
}
},
{
"blockName": "core-embed/youtube",
"meta": {
"url": "https://www.youtube.com/watch?v=9pT-q0SSYow",
"caption": "<strong>This is the video caption</strong>"
}
},
{
"blockName": "core/quote",
"meta": {
"quote": "Saramago sonogo\\nEn la lista del longo",
"cite": "<em>alguno</em>"
}
},
{
"blockName": "core/image",
"meta": {
"src": "https://ps.w.org/gutenberg/assets/banner-1544x500.jpg"
}
},
{
"blockName": "core/heading",
"meta": {
"size": "xl",
"heading": "Some heading here"
}
},
{
"blockName": "core/gallery",
"meta": {
"imgs": [
{
"src": "https://newapi.getpop.org/wp/wp-content/uploads/2019/08/Sample-jpg-image-50kb.jpg",
"width": 300,
"height": 300
},
{
"src": "https://newapi.getpop.org/wp/wp-content/uploads/2019/08/setting-rest-fields.png",
"width": 1738,
"height": 246
},
{
"src": "https://newapi.getpop.org/wp/wp-content/uploads/2019/08/Sample-jpg-image-100kb.jpg",
"width": 689,
"height": 689
},
{
"src": "https://newapi.getpop.org/wp/wp-content/uploads/2019/08/banner-1544x500.jpg",
"width": 1544,
"height": 500
}
]
}
},
{
"blockName": "core/list",
"meta": {
"items": [
"First element",
"Second element",
"Third element"
]
}
},
{
"blockName": "core/audio",
"meta": {
"src": false
}
},
{
"blockName": "core/paragraph",
"meta": {
"content": "Watch out the contrast!"
}
},
{
"blockName": "core/file",
"meta": {
"href": "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
"text": "Contributor-Day <strong>download</strong> file"
}
},
{
"blockName": "core/video",
"meta": {
"src": "https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4",
"settings": [
"autoplay",
"muted",
"playsinline"
],
"caption": "Caption for <em>the</em> video"
}
},
{
"blockName": "core/code",
"meta": {
"code": "function recursive_parse_blocks( $content ) {\n\t$ret = [];\n\t$blocks = parse_blocks( $content );\n\trecursive_add_blocks($ret, $blocks);\n\treturn $ret;\n}"
}
},
{
"blockName": "core/preformatted",
"meta": {
"text": "Some pre-formated text"
}
},
{
"blockName": "core/pullquote",
"meta": {
"quote": "The will to win, the desire to succeed, the urge to reach your full potential… these are the keys that will unlock the door to personal excellence.",
"cite": "Confucius"
}
},
{
"blockName": "core/verse",
"meta": {
"text": "It is easy to hate and it is difficult to love. This is how the whole scheme of things works. All good things are difficult to achieve; and bad things are very easy to get."
}
},
{
"blockName": "core/paragraph",
"meta": {
"content": "First grouped paragraph"
}
},
{
"blockName": "core/paragraph",
"meta": {
"content": "Second grouped paragraph"
}
},
{
"blockName": "core/paragraph",
"meta": {
"content": "Here is the media header"
}
},
{
"blockName": "core/paragraph",
"meta": {
"content": "And some text"
}
}
]
<!-- wp:block {"ref":1500} /-->
<!-- wp:image {"id":262,"sizeSlug":"large"} -->
<figure class="wp-block-image size-large"><img src="https://ps.w.org/gutenberg/assets/banner-1544x500.jpg" alt="" class="wp-image-262"/></figure>
<!-- /wp:image -->
<!-- wp:paragraph -->
<p><em>Etiam tempor orci eu lobortis elementum nibh tellus molestie. Neque egestas congue quisque egestas. Egestas integer eget aliquet nibh praesent tristique. Vulputate mi sit amet mauris. Sodales neque sodales ut etiam sit. Dignissim suspendisse in est ante in. Volutpat commodo sed egestas egestas. Felis donec et odio pellentesque diam. Pharetra vel turpis nunc eget lorem dolor sed viverra. Porta nibh venenatis cras sed felis eget. Aliquam ultrices sagittis orci a. Dignissim diam quis enim lobortis. Aliquet porttitor lacus luctus accumsan. Dignissim convallis aenean et tortor at risus viverra adipiscing at.</em></p>
<!-- /wp:paragraph -->
<!-- wp:core-embed/youtube {"url":"https://www.youtube.com/watch?v=9pT-q0SSYow","type":"video","providerNameSlug":"youtube","className":"wp-embed-aspect-16-9 wp-has-aspect-ratio"} -->
<figure class="wp-block-embed-youtube wp-block-embed is-type-video is-provider-youtube wp-embed-aspect-16-9 wp-has-aspect-ratio"><div class="wp-block-embed__wrapper">
https://www.youtube.com/watch?v=9pT-q0SSYow
</div><figcaption><strong>This is the video caption</strong></figcaption></figure>
<!-- /wp:core-embed/youtube -->
<!-- wp:columns -->
<div class="wp-block-columns"><!-- wp:column -->
<div class="wp-block-column"><!-- wp:quote -->
<blockquote class="wp-block-quote"><p>Saramago sonogo</p><p>En la lista del longo</p><cite><em><a href="https://yahoo.com">alguno</a></em></cite></blockquote>
<!-- /wp:quote --></div>
<!-- /wp:column -->
<!-- wp:column -->
<div class="wp-block-column"><!-- wp:image {"id":70,"sizeSlug":"large"} -->
<figure class="wp-block-image size-large"><img src="https://ps.w.org/gutenberg/assets/banner-1544x500.jpg" alt="" class="wp-image-70"/></figure>
<!-- /wp:image --></div>
<!-- /wp:column --></div>
<!-- /wp:columns -->
<!-- wp:heading -->
<h2>Some heading here</h2>
<!-- /wp:heading -->
<!-- wp:gallery {"ids":[1502,1505,1503,1504]} -->
<ul class="wp-block-gallery columns-3 is-cropped"><li class="blocks-gallery-item"><figure><img src="https://newapi.getpop.org/wp/wp-content/uploads/2019/08/Sample-jpg-image-50kb.jpg" alt="" data-id="1502" data-link="https://newapi.getpop.org/uncategorized/cope-with-wordpress-post-demo-containing-plenty-of-blocks/attachment/sample-jpg-image-50kb/" class="wp-image-1502"/><figcaption>Caption 1st image</figcaption></figure></li><li class="blocks-gallery-item"><figure><img src="https://newapi.getpop.org/wp/wp-content/uploads/2019/08/setting-rest-fields-1024x145.png" alt="" data-id="1505" data-link="https://newapi.getpop.org/uncategorized/cope-with-wordpress-post-demo-containing-plenty-of-blocks/attachment/setting-rest-fields/" class="wp-image-1505"/></figure></li><li class="blocks-gallery-item"><figure><img src="https://newapi.getpop.org/wp/wp-content/uploads/2019/08/Sample-jpg-image-100kb.jpg" alt="" data-id="1503" data-link="https://newapi.getpop.org/uncategorized/cope-with-wordpress-post-demo-containing-plenty-of-blocks/attachment/sample-jpg-image-100kb/" class="wp-image-1503"/><figcaption>Caption 3rd image</figcaption></figure></li><li class="blocks-gallery-item"><figure><img src="https://newapi.getpop.org/wp/wp-content/uploads/2019/08/banner-1544x500-1024x332.jpg" alt="" data-id="1504" data-link="https://newapi.getpop.org/uncategorized/cope-with-wordpress-post-demo-containing-plenty-of-blocks/attachment/banner-1544x500/" class="wp-image-1504"/><figcaption>Final <strong>caption</strong> <a href="https://getpop.org">for all</a></figcaption></figure></li></ul>
<!-- /wp:gallery -->
<!-- wp:list -->
<ul><li>First element</li><li>Second element</li><li>Third element</li></ul>
<!-- /wp:list -->
<!-- wp:audio {"id":224} -->
<figure class="wp-block-audio"><audio controls src="https://sample-videos.com/audio/mp3/crowd-cheering.mp3"></audio></figure>
<!-- /wp:audio -->
<!-- wp:cover {"url":"https://ps.w.org/gutenberg/assets/banner-1544x500.jpg","id":71} -->
<div class="wp-block-cover has-background-dim" style="background-image:url(https://ps.w.org/gutenberg/assets/banner-1544x500.jpg)"><div class="wp-block-cover__inner-container"><!-- wp:paragraph {"align":"center","placeholder":"Write title…","fontSize":"large"} -->
<p style="text-align:center" class="has-large-font-size">Watch out the contrast!</p>
<!-- /wp:paragraph --></div></div>
<!-- /wp:cover -->
<!-- wp:file {"id":225,"href":"https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf","showDownloadButton":false} -->
<div class="wp-block-file"><a href="https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf">Contributor-Day <strong>download</strong> file</a></div>
<!-- /wp:file -->
<!-- wp:video -->
<figure class="wp-block-video"><video autoplay muted poster="" src="https://www.sample-videos.com/video123/mp4/720/big_buck_bunny_720p_1mb.mp4" playsinline></video><figcaption>Caption for <em>the</em> video</figcaption></figure>
<!-- /wp:video -->
<!-- wp:code -->
<pre class="wp-block-code"><code>function recursive_parse_blocks( $content ) {
$ret = [];
$blocks = parse_blocks( $content );
recursive_add_blocks($ret, $blocks);
return $ret;
}</code></pre>
<!-- /wp:code -->
<p>This is a TinyMCE textarea</p>
<p>The text is formatted</p>
<!-- wp:html -->
<p>This is <strong>HTML</strong> so we don't really need it for <em>mediums</em> other than web!</p>
<!-- /wp:html -->
<!-- wp:preformatted -->
<pre class="wp-block-preformatted">Some pre-formated text</pre>
<!-- /wp:preformatted -->
<!-- wp:pullquote -->
<figure class="wp-block-pullquote"><blockquote><p>The will to win, the desire to succeed, the urge to reach your full potential… these are the keys that will unlock the door to personal excellence.</p><cite>Confucius</cite></blockquote></figure>
<!-- /wp:pullquote -->
<!-- wp:table -->
<figure class="wp-block-table"><table class=""><tbody><tr><td>Content column 1 row 1</td><td>Content column 2 row 1</td></tr><tr><td>Content column 1 row 2</td><td>Content column 2 row 2</td></tr></tbody></table></figure>
<!-- /wp:table -->
<!-- wp:verse -->
<pre class="wp-block-verse">It is easy to hate and it is difficult to love. This is how the whole scheme of things works. All good things are difficult to achieve; and bad things are very easy to get.</pre>
<!-- /wp:verse -->
<!-- wp:button -->
<div class="wp-block-button"><a class="wp-block-button__link" href="https://getpop.org">This is a button, onclick goes somewhere</a></div>
<!-- /wp:button -->
<!-- wp:group -->
<div class="wp-block-group"><div class="wp-block-group__inner-container"><!-- wp:paragraph -->
<p>First grouped paragraph</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>Second grouped paragraph</p>
<!-- /wp:paragraph --></div></div>
<!-- /wp:group -->
<!-- wp:media-text {"mediaId":70,"mediaType":"image"} -->
<div class="wp-block-media-text alignwide"><figure class="wp-block-media-text__media"><img src="https://ps.w.org/gutenberg/assets/banner-1544x500.jpg" alt="" class="wp-image-70"/></figure><div class="wp-block-media-text__content"><!-- wp:paragraph {"placeholder":"Content…","fontSize":"large"} -->
<p class="has-large-font-size">Here is the media header</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p>And some text</p>
<!-- /wp:paragraph --></div></div>
<!-- /wp:media-text -->
<!-- wp:separator {"className":"is-style-wide"} -->
<hr class="wp-block-separator is-style-wide"/>
<!-- /wp:separator -->
<!-- wp:spacer {"height":30} -->
<div style="height:30px" aria-hidden="true" class="wp-block-spacer"></div>
<!-- /wp:spacer -->
<!-- wp:core-embed/twitter {"url":"https://twitter.com/losoviz/status/1148250406281105408","type":"rich","providerNameSlug":"twitter","className":""} -->
<figure class="wp-block-embed-twitter wp-block-embed is-type-rich is-provider-twitter"><div class="wp-block-embed__wrapper">
https://twitter.com/losoviz/status/1148250406281105408
</div><figcaption>Some tweet</figcaption></figure>
<!-- /wp:core-embed/twitter -->
By Leonardo Losoviz
Presentation for WordCamp Singapore 2019 and Kuala Lumpur 2019
Creator of Gato GraphQL, and contributor to several online magazines, including Smashing Magazine and CSS Tricks.