{"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<p class=\"wp-block-paragraph\">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<!--more-->\n\n\n\n<div class=\"update-box\">\n\n<p><strong>Update from January 9, 2020:<\/strong> As Jeslen pointed out <a href=\"https:\/\/florianbrinkmann.com\/en\/display-specific-gutenberg-blocks-of-a-post-outside-of-the-post-content-in-the-theme-5620\/#comment-12447\">in the comments<\/a>, the <code class=\"lang-php\">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<p class=\"wp-block-paragraph\">Usually, a theme uses <code class=\"lang-php\">the_content()<\/code> to display the post content. More helpful for us is <code class=\"lang-php\">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<p class=\"wp-block-paragraph\">Since WordPress 5.0, core runs the <code class=\"lang-php\">do_blocks<\/code> function on this content, that looks like that in 5.0.3:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/**\n * Parses dynamic blocks out of `post_content` and re-renders them.\n *\n * <span class=\"hljs-doctag\">@since<\/span> 5.0.0\n * <span class=\"hljs-doctag\">@global<\/span> WP_Post $post The post to edit.\n *\n * <span class=\"hljs-doctag\">@param<\/span>  string $content Post content.\n * <span class=\"hljs-doctag\">@return<\/span> string Updated post content.\n *\/<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">do_blocks<\/span><span class=\"hljs-params\">( $content )<\/span> <\/span>{\n\t<span class=\"hljs-comment\">\/\/ If there are blocks in this content, we shouldn't run wpautop() on it later.<\/span>\n\t$priority = has_filter( <span class=\"hljs-string\">'the_content'<\/span>, <span class=\"hljs-string\">'wpautop'<\/span> );\n\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">false<\/span> !== $priority &amp;&amp; doing_filter( <span class=\"hljs-string\">'the_content'<\/span> ) &amp;&amp; has_blocks( $content ) ) {\n\t\tremove_filter( <span class=\"hljs-string\">'the_content'<\/span>, <span class=\"hljs-string\">'wpautop'<\/span>, $priority );\n\t\tadd_filter( <span class=\"hljs-string\">'the_content'<\/span>, <span class=\"hljs-string\">'_restore_wpautop_hook'<\/span>, $priority + <span class=\"hljs-number\">1<\/span> );\n\t}\n\n\t$blocks = parse_blocks( $content );\n\t$output = <span class=\"hljs-string\">''<\/span>;\n\n\t<span class=\"hljs-keyword\">foreach<\/span> ( $blocks <span class=\"hljs-keyword\">as<\/span> $block ) {\n\t\t$output .= render_block( $block );\n\t}\n\n\t<span class=\"hljs-keyword\">return<\/span> $output;\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">You can see that a <code class=\"lang-php\">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<h2 class=\"wp-block-heading\">Get the content blocks as an array<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">We can use the following line of code to get the blocks of a post as an array:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$blocks = parse_blocks( get_the_content() );<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">This is what an array entry of a paragraph block looks like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-number\">2<\/span> =&gt; \n\t<span class=\"hljs-keyword\">array<\/span> (size=<span class=\"hljs-number\">5<\/span>)\n\t<span class=\"hljs-string\">'blockName'<\/span> =&gt; string <span class=\"hljs-string\">'core\/paragraph'<\/span> (length=<span class=\"hljs-number\">14<\/span>)\n\t<span class=\"hljs-string\">'attrs'<\/span> =&gt; \n\t\t<span class=\"hljs-keyword\">array<\/span> (size=<span class=\"hljs-number\">0<\/span>)\n\t\t<span class=\"hljs-keyword\">empty<\/span>\n\t<span class=\"hljs-string\">'innerBlocks'<\/span> =&gt; \n\t\t<span class=\"hljs-keyword\">array<\/span> (size=<span class=\"hljs-number\">0<\/span>)\n\t\t<span class=\"hljs-keyword\">empty<\/span>\n\t<span class=\"hljs-string\">'innerHTML'<\/span> =&gt; string <span class=\"hljs-string\">'&lt;p&gt;Ein Absatz&lt;\/p&gt;'<\/span> (length=<span class=\"hljs-number\">19<\/span>)\n\t<span class=\"hljs-string\">'innerContent'<\/span> =&gt; \n\t\t<span class=\"hljs-keyword\">array<\/span> (size=<span class=\"hljs-number\">1<\/span>)\n\t\t<span class=\"hljs-number\">0<\/span> =&gt; string <span class=\"hljs-string\">'&lt;p&gt;Ein Absatz&lt;\/p&gt;'<\/span> (length=<span class=\"hljs-number\">19<\/span>)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">The entry contains various information about the block, like the name and its content.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Display the slider block separate from the content<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To display a slider from the content above the post title, we can do something like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Get post content to extract slider shortcode.<\/span>\n$blocks = parse_blocks( get_the_content() );\n<span class=\"hljs-keyword\">foreach<\/span> ( $blocks <span class=\"hljs-keyword\">as<\/span> $block ) {\n\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-string\">'soliloquy\/soliloquywp'<\/span> === $block&#91;<span class=\"hljs-string\">'blockName'<\/span>] ) {\n\t\t<span class=\"hljs-keyword\">echo<\/span> do_shortcode( $block&#91;<span class=\"hljs-string\">'innerHTML'<\/span>] );\n\t\t<span class=\"hljs-keyword\">break<\/span>;\n\t}\n}\n<span class=\"hljs-comment\">\/\/ Display the title.<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">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 <code class=\"lang-php\">soliloquy\/soliloquywp<\/code>, and that is the value we check for in the <code class=\"lang-php\">foreach<\/code> loop.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">After finding it, we display the Shortcode from <code class=\"lang-php\">$block['innerHTML']<\/code> and end the loop. Then we can display the title.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Displaying the rest of the content<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Now we need to display the remaining content:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$content_markup = <span class=\"hljs-string\">''<\/span>;\n<span class=\"hljs-keyword\">foreach<\/span> ( $blocks <span class=\"hljs-keyword\">as<\/span> $block ) {\n\t<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-string\">'soliloquy\/soliloquywp'<\/span> === $block&#91;<span class=\"hljs-string\">'blockName'<\/span>] ) {\n\t\t<span class=\"hljs-keyword\">continue<\/span>;\n\t} <span class=\"hljs-keyword\">else<\/span> {\n\t\t$content_markup .= render_block( $block );\n\t}\n}\n\n<span class=\"hljs-comment\">\/\/ Remove wpautop filter so we do not get paragraphs for two line breaks in the content.<\/span>\n$priority = has_filter( <span class=\"hljs-string\">'the_content'<\/span>, <span class=\"hljs-string\">'wpautop'<\/span> );\n<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">false<\/span> !== $priority ) {\n\tremove_filter( <span class=\"hljs-string\">'the_content'<\/span>, <span class=\"hljs-string\">'wpautop'<\/span>, $priority );\n}\n\n<span class=\"hljs-keyword\">echo<\/span> apply_filters( <span class=\"hljs-string\">'the_content'<\/span>, $content_markup );\n\n<span class=\"hljs-keyword\">if<\/span> ( <span class=\"hljs-keyword\">false<\/span> !== $priority ) {\n\tadd_filter( <span class=\"hljs-string\">'the_content'<\/span>, <span class=\"hljs-string\">'wpautop'<\/span>, $priority );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">We loop through the <code class=\"lang-php\">$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 <code class=\"lang-php\">render_block( $block )<\/code> call to a variable. The function returns the HTML string for a given block.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To correctly display things like embeds and shortcodes, we run all the <code class=\"lang-php\">the_content<\/code> filters above <code class=\"lang-php\">$content_markup<\/code> before displaying it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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 <code class=\"lang-php\">the_content()<\/code>, you would need to modify those places, too.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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":"<p>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":{"wpf_show_in_dewp_planet_feed":false,"flobn_post_versions":"","webmentions_disabled_pings":false,"webmentions_disabled":false,"lazy_load_responsive_images_disabled":false,"footnotes":""},"categories":[6],"tags":[],"class_list":["post-5620","post","type-post","status-publish","format-standard","hentry","category-web-development"],"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","targetHints":{"allow":["GET"]}}],"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}]}}