{"id":5620,"date":"2019-01-20T11:43:45","date_gmt":"2019-01-20T10:43:45","guid":{"rendered":"https:\/\/florianbrinkmann.com\/en\/?p=5620"},"modified":"2020-02-09T11:11:23","modified_gmt":"2020-02-09T10:11:23","slug":"display-specific-gutenberg-blocks-of-a-post-outside-of-the-post-content-in-the-theme","status":"publish","type":"post","link":"https:\/\/florianbrinkmann.com\/en\/display-specific-gutenberg-blocks-of-a-post-outside-of-the-post-content-in-the-theme-5620\/","title":{"rendered":"Display specific Gutenberg blocks of a post outside of the post content in the theme"},"content":{"rendered":"\n

Sometimes it could be useful to display parts of a post or page on another place on the site. For example, a slider above the title of the post\/page. Here I show you how to loop through the blocks of a post block per block in a theme to get this done.<\/p>\n\n\n\n\n\n\n\n

\n\n

Update from January 9, 2020:<\/strong> As Jeslen pointed out in the comments<\/a>, the wpautop<\/code> filter should be removed from running on our content markup. Otherwise, we might get unexpected paragraph elements in our output. I updated the code accordingly.<\/p>\n\n<\/div>\n\n\n\n

Usually, a theme uses the_content()<\/code> to display the post content. More helpful for us is get_the_content()<\/code>, to get the content of a post or page including the block comments of Gutenberg as it is stored in the database.<\/p>\n\n\n\n

Since WordPress 5.0, core runs the do_blocks<\/code> function on this content, that looks like that in 5.0.3:<\/p>\n\n\n

\/**\n * Parses dynamic blocks out of `post_content` and re-renders them.\n *\n * @since<\/span> 5.0.0\n * @global<\/span> WP_Post $post The post to edit.\n *\n * @param<\/span> string $content Post content.\n * @return<\/span> string Updated post content.\n *\/<\/span>\nfunction<\/span> do_blocks<\/span>( $content )<\/span> <\/span>{\n\t\/\/ If there are blocks in this content, we shouldn't run wpautop() on it later.<\/span>\n\t$priority = has_filter( 'the_content'<\/span>, 'wpautop'<\/span> );\n\tif<\/span> ( false<\/span> !== $priority && doing_filter( 'the_content'<\/span> ) && has_blocks( $content ) ) {\n\t\tremove_filter( 'the_content'<\/span>, 'wpautop'<\/span>, $priority );\n\t\tadd_filter( 'the_content'<\/span>, '_restore_wpautop_hook'<\/span>, $priority + 1<\/span> );\n\t}\n\n\t$blocks = parse_blocks( $content );\n\t$output = ''<\/span>;\n\n\tforeach<\/span> ( $blocks as<\/span> $block ) {\n\t\t$output .= render_block( $block );\n\t}\n\n\treturn<\/span> $output;\n}<\/code><\/div>Code language:<\/span> PHP<\/span> (<\/span>php<\/span>)<\/span><\/small><\/pre>\n\n\n

You can see that a parse_blocks()<\/code> function is called, that returns the blocks of the content so that they can be accessed one by one in a loop. And this is the part we need.<\/p>\n\n\n\n

Get the content blocks as an array<\/h2>\n\n\n\n

We can use the following line of code to get the blocks of a post as an array:<\/p>\n\n\n

$blocks = parse_blocks( get_the_content() );<\/code><\/div>Code language:<\/span> PHP<\/span> (<\/span>php<\/span>)<\/span><\/small><\/pre>\n\n\n

This is what an array entry of a paragraph block looks like:<\/p>\n\n\n

2<\/span> => \n\tarray<\/span> (size=5<\/span>)\n\t'blockName'<\/span> => string 'core\/paragraph'<\/span> (length=14<\/span>)\n\t'attrs'<\/span> => \n\t\tarray<\/span> (size=0<\/span>)\n\t\tempty<\/span>\n\t'innerBlocks'<\/span> => \n\t\tarray<\/span> (size=0<\/span>)\n\t\tempty<\/span>\n\t'innerHTML'<\/span> => string '<p>Ein Absatz<\/p>'<\/span> (length=19<\/span>)\n\t'innerContent'<\/span> => \n\t\tarray<\/span> (size=1<\/span>)\n\t\t0<\/span> => string '<p>Ein Absatz<\/p>'<\/span> (length=19<\/span>)<\/code><\/div>Code language:<\/span> PHP<\/span> (<\/span>php<\/span>)<\/span><\/small><\/pre>\n\n\n

The entry contains various information about the block, like the name and its content.<\/p>\n\n\n\n

Display the slider block separate from the content<\/h2>\n\n\n\n

To display a slider from the content above the post title, we can do something like this:<\/p>\n\n\n

\/\/ Get post content to extract slider shortcode.<\/span>\n$blocks = parse_blocks( get_the_content() );\nforeach<\/span> ( $blocks as<\/span> $block ) {\n\tif<\/span> ( 'soliloquy\/soliloquywp'<\/span> === $block['blockName'<\/span>] ) {\n\t\techo<\/span> do_shortcode( $block['innerHTML'<\/span>] );\n\t\tbreak<\/span>;\n\t}\n}\n\/\/ Display the title.<\/span><\/code><\/div>Code language:<\/span> PHP<\/span> (<\/span>php<\/span>)<\/span><\/small><\/pre>\n\n\n

The project where I had to create that behavior for uses the Soliloquy slider, which comes with a Gutenberg block (in the end it stores a shortcode in the database). The block has the block name soliloquy\/soliloquywp<\/code>, and that is the value we check for in the foreach<\/code> loop.<\/p>\n\n\n\n

After finding it, we display the Shortcode from $block['innerHTML']<\/code> and end the loop. Then we can display the title.<\/p>\n\n\n\n

Displaying the rest of the content<\/h2>\n\n\n\n

Now we need to display the remaining content:<\/p>\n\n\n

$content_markup = ''<\/span>;\nforeach<\/span> ( $blocks as<\/span> $block ) {\n\tif<\/span> ( 'soliloquy\/soliloquywp'<\/span> === $block['blockName'<\/span>] ) {\n\t\tcontinue<\/span>;\n\t} else<\/span> {\n\t\t$content_markup .= render_block( $block );\n\t}\n}\n\n\/\/ Remove wpautop filter so we do not get paragraphs for two line breaks in the content.<\/span>\n$priority = has_filter( 'the_content'<\/span>, 'wpautop'<\/span> );\nif<\/span> ( false<\/span> !== $priority ) {\n\tremove_filter( 'the_content'<\/span>, 'wpautop'<\/span>, $priority );\n}\n\necho<\/span> apply_filters( 'the_content'<\/span>, $content_markup );\n\nif<\/span> ( false<\/span> !== $priority ) {\n\tadd_filter( 'the_content'<\/span>, 'wpautop'<\/span>, $priority );\n}<\/code><\/div>Code language:<\/span> PHP<\/span> (<\/span>php<\/span>)<\/span><\/small><\/pre>\n\n\n

We loop through the $blocks<\/code> array again checking for the slider block. But this time to ignore that block in the output. For all other blocks, we add the return value of the render_block( $block )<\/code> call to a variable. The function returns the HTML string for a given block.<\/p>\n\n\n\n

To correctly display things like embeds and shortcodes, we run all the the_content<\/code> filters above $content_markup<\/code> before displaying it.<\/p>\n\n\n\n

This solution has the potential for optimization, so we could remove the core filters for block rendering to not run them twice. But if you \u2014 like me \u2014 want to use this special behavior of the content only in a few places and in all other cases simply call the_content()<\/code>, you would need to modify those places, too.<\/p>\n\n\n\n

If you do not have an enormous amount of blocks in your posts, the performance loss due to the double execution of the block parser logic probably will not be of much importance.<\/p>\n","protected":false},"excerpt":{"rendered":"

Sometimes it could be useful to display parts of a post or page on another place on the site. For example, a slider above the title of the post\/page. Here I show you how to loop through the blocks of a post block per block in a theme to get this done.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","wpf_show_in_dewp_planet_feed":false,"flobn_post_versions":"","lazy_load_responsive_images_disabled":false},"categories":[6],"tags":[],"wp-worthy-pixel":{"ignored":false,"public":"3efeca664d95405ba241bd2cf8f29ff3","server":"vg07.met.vgwort.de","url":"https:\/\/vg07.met.vgwort.de\/na\/3efeca664d95405ba241bd2cf8f29ff3"},"wp-worthy-type":"normal","_links":{"self":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/5620"}],"collection":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/comments?post=5620"}],"version-history":[{"count":5,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/5620\/revisions"}],"predecessor-version":[{"id":5953,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/posts\/5620\/revisions\/5953"}],"wp:attachment":[{"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/media?parent=5620"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/categories?post=5620"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/florianbrinkmann.com\/en\/wp-json\/wp\/v2\/tags?post=5620"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}