Blog /

Problem solved: check for has_block() inside reusable blocks

Do you check block type with has_block() to conditionally load scripts, styles, or any other custom logic for the block editor as I do? I’m not sure if you have already noticed, but has_block() doesn’t count with reusable blocks. In the docs, they don’t mention it explicitly: This test optimizes for performance rather than strict accuracy,…

Do you check block type with has_block() to conditionally load scripts, styles, or any other custom logic for the block editor as I do? I’m not sure if you have already noticed, but has_block() doesn’t count with reusable blocks. In the docs, they don’t mention it explicitly: This test optimizes for performance rather than strict accuracy, detecting the block type exists but not validating its structure. For strict accuracy, you should use the block parser on post content.

Maybe you haven’t encountered this issue yet – it took me a couple of months to encounter it myself – but you should know about it. The reason is pretty straightforward: has_blocks() checks the not parsed post content, so the reusable blocks are just a core/block type and nothing more at that point.

I was looking for an existing solution, and except for two open tickets in Gutenberg issues (#18272, #17048), I haven’t found any. It helped a bit, but I wanted a single check to search for a block in reusable blocks.

I’ve ended up with the following solution

  1. Check if the native has_block() function will do.
  2. If not – parse content (in that point, you get both the blocks & reusable blocks)
  3. Search for your desired block type, and don’t forget about nested blocks.

OOP approach

If you’re using this check within the same context, it’s better to use the OOP to perform better. It allows you to instantiate the class just once per post and reuse parsed content as many times as you need if you have no luck with the native has_block() function.


<?php
// Do not copy the line above if you don't need it.
/**
* Block Helper extending the functionality of core's has_blocks with search within inner blocks.
* @package WordPress
* @author Karolína Vyskočilová <[email protected]>
* @since 2021-04-12
*/
class BlockHelper
{
private $blocks;
/**
* Has block implementation counting with reusable blocks
*
* @param string $block_name Full Block type to look for.
* @return bool
*/
public function has_block($block_name)
{
if (has_block($block_name)) {
return true;
}
$this->parse_reusable_blocks();
if (empty($this->blocks)) {
return false;
}
return $this->recursive_search_within_innerblocks($this->blocks, $block_name);
}
/**
* Search for a reusable block inside innerblocks
*
* @param array $blocks Blocks to loop through.
* @param string $block_name BFull Block type to look for.
* @return true|void
*/
private function recursive_search_within_innerblocks($blocks, $block_name)
{
foreach ($blocks as $block) {
if (isset($block['innerBlocks']) && !empty($block['innerBlocks'])) {
$this->recursive_search_within_innerblocks($block['innerBlocks'], $block_name);
} elseif ($block['blockName'] === 'core/block' && !empty($block['attrs']['ref']) && \has_block($block_name, $block['attrs']['ref'])) {
return true;
}
}
return false;
}
/**
* Parse blocks if at leat one reusable block is presnt.
*
* @return void
*/
private function parse_reusable_blocks()
{
if ($this->blocks !== null) {
return;
}
if (has_block('core/block')) {
$content = get_post_field('post_content');
$this->blocks = parse_blocks($content);
return;
}
$this->blocks = false;
}
}

Usage is simple:

  1. $helper = new BlockHelper();
  2. and then use $helper->has_block('block/name') anytime you need to check.

Functional approach

If you tend to stick with functional programming (cheers, Adam!), although it’s not always as effective as possible, here are two functions.

To perform the check call the enhanced_has_block('block/name') function. The second one is a helper function that performs a recursive search within inner blocks.


<?php
// Do not copy the line above if you don't need it.
/**
* Has block function which searches as well in reusable blocks.
*
* Extends functionality of core's has_block (https://developer.wordpress.org/reference/functions/has_block/)
*
* @author Karolína Vyskočilová <[email protected]>
* @since 2021-04-12
* @param mixed $block_name Full Block type to look for.
* @return bool
*/
function enhanced_has_block($block_name)
{
if (has_block($block_name)) {
return true;
}
if (has_block('core/block')) {
$content = get_post_field('post_content');
$blocks = parse_blocks($content);
return search_reusable_blocks_within_innerblocks($blocks, $block_name);
}
return false;
}
/**
* Search for selected block within inner blocks.
*
* The helper function for enhanced_has_block() function.
*
* @author Karolína Vyskočilová <[email protected]>
* @since 2021-04-12
* @param array $blocks Blocks to loop through.
* @param string $block_name Full Block type to look for.
* @return bool
*/
function search_reusable_blocks_within_innerblocks($blocks, $block_name)
{
foreach ($blocks as $block) {
if (isset($block['innerBlocks']) && !empty($block['innerBlocks'])) {
search_reusable_blocks_within_innerblocks($block['innerBlocks'], $block_name);
} elseif ($block['blockName'] === 'core/block' && !empty($block['attrs']['ref']) && \has_block($block_name, $block['attrs']['ref'])) {
return true;
}
}
return false;
}

Please let me know if I missed something or you have any improvements to be made.