Background: Creating Custom Gutenberg Blocks and the Challenges with Breaking Changes
I am new to creating custom Gutenberg blocks (each block is a separate plugin) and have recently ran into the issue of breaking changes when I update the blocks’ code. I learned how block depreciation can help solve the issue of breaking code changes, but I still have the challenge of finding the specific pages and posts where these blocks are used.
Reason for the Question: To Find All Instances of a Custom Gutenberg Block and Speed Up the Quality Control Process
I am looking for a way to see a list of the posts and pages where they have been used so that I can verify that the upgrade path is working for all of the different use cases people have come up with… without manually going through each and every page and then scrolling to see if one of my blocks is used or not. Some sites have hundreds of pages and posts. I would like to make it a more efficient process to find just the pages and posts where a custom block is used.
2 Answers
General block name search
One can e.g. use the general search in the /wp-admin
backend to find some text within the post’s content that stores the blocks:
We can search for a prefixed block name wp:my-plugin/my-block
within the posts:
example.com/wp-admin/edit.php
?s=wp:my-plugin%2Fmy-block%20&post_type=post
where we have an extra space (url encoded as %20
and / as %2F
) after the block’s name, as the first part of it would be stored as <!-- wp:my-plugin/my-block -->
or with attributes <!-- wp:my-plugin/my-block {...} -->
in the post’s content.
The front-end search works as well, but without the post type filtering as in the backend (unless adjusted with a plugin):
example.com
?s=wp:my-plugin%2Fmy-block%20
This block search might not be 100% accurate (e.g. if you blog about block names) but it might give you a good enough estimate in most cases.
More accurate searching
We note that searching for <!-- wp:my-plugin/my-block
with:
example.com
?s=<%21--%20wp:my-plugin%2Fmy-block%20
will be split up in two words in the generated SQL:
WHERE 1=1
AND (((wp_posts.post_title LIKE '%<!--%')
OR (wp_posts.post_excerpt LIKE '%<!--%')
OR (wp_posts.post_content LIKE '%<!--%'))
AND ((wp_posts.post_title LIKE '%wp:my-plugin/my-block %')
OR (wp_posts.post_excerpt LIKE '%wp:my-plugin/my-block %')
OR (wp_posts.post_content LIKE '%wp:my-plugin/my-block %')))
...
but we can avoid it with the sentence
parameter:
example.com
?s=<%21--%20wp:my-plugin%2Fmy-block&sentence=1
and get a search query like:
WHERE 1=1
AND (((wp_posts.post_title LIKE '%<!-- wp:my-plugin/my-block %')
OR (wp_posts.post_excerpt LIKE '%<!-- wp:my-plugin/my-block %')
OR (wp_posts.post_content LIKE '%<!-- wp:my-plugin/my-block %')))
...
With code
Alternatively we could use a custom WP_Query
search like:
WP_Query( array(
's' => '<!-- wp:my-plugin/my-block ',
'sentence' => 1,
'post_type' => array( 'post', 'page' ),
) );
or run has_block( 'my-plugin/my-block', $post_content )
on all the relevant post content for the given block name or use parse_blocks( $post_content )
if you need more info from it.
Reusable blocks
We should note that reusable blocks are stored like:
<!-- wp:block {"ref":23495} /-->
in the post’s content, so we might look into the wp_block
post type that stores it:
example.com/wp-admin/edit.php
?s=<%21--%20wp:my-plugin%2Fmy-block%20&post_status=all&post_type=wp_block&sentence=1
Simple Block Type Search Plugin
To make things easier I’ve created a plugin that adds a dropdown filter for registered block types, under each list table in the admin:
to simplify the search that is automatically added when filtering:
Create a plugin file and activate:
<?php
/* Plugin Name: Simple Block Type Search
* Plugin URI: https://wordpress.stackexchange.com/a/392175/26350
*/
add_action( 'restrict_manage_posts', 'simple_block_type_search', 10, 2 );
function simple_block_type_search ( $post_type, $which ) {
if ( ! use_block_editor_for_post_type ( $post_type ) ) {
return;
}
$block_types = \WP_Block_Type_Registry::get_instance()->get_all_registered();
$options = array_reduce( $block_types, function( $options, $block_type ) {
$val="!-- wp:" . str_replace( 'core/', '', $block_type->name ) . ' ';
return $options . sprintf ( '<option value="%s" %s>%s</option>',
esc_attr( $val ),
selected( get_query_var( 's' ), $val, false ),
esc_html( $block_type->name )
);
}, '' );
printf ( '<select name="s"><option value="">%s</option>%s</select>
<input type="hidden" name="sentence" value="1"/>',
esc_html__( 'Block Type Search', 'simple_block_type_search' ),
$options
);
}
To get all registered block types, we used:
\WP_Block_Type_Registry::get_instance()->get_all_registered()
and to only display the dropdown on supported post types we used:
use_block_editor_for_post_type()
Here we used the built-in search feature and the user interface of the admin backend. That’s why the plugin is so few lines of code.