• Categories
    • Coding
    • Design
    • Inspiration
    • News
    • WP Plugins
    • WP Themes
  • Start Here
    • How to Start a Blog
    • Make A Website
    • WordPress Hosting
  • Freebies
  • Deals
    • WPEngine Coupon
    • WPX Coupon
    • Elegant Themes Coupon
    • View All Deals
  • Glossary
How to Register Gutenberg Blocks for Some Custom Post Type only, using PHP (not JS!)

By Leonardo Losoviz May 17, 2020

How to Register Gutenberg Blocks for Some Custom Post Type only, using PHP (not JS!)

When creating a WordPress site, we may need to create a custom post type and have it reference other entities from the database. For instance, a Product custom post, which displays how many stars it has been rated by the users, needs to reference a list of users or a list of ProductReview custom posts.

WordPress provides custom meta fields, which enable to add any arbitrary piece of data to posts, users, comments, categories and tags, such as references to any other entity from the database. However, WordPress doesn’t provide a built-in solution to create the relationships across entities in the admin panel in a user-friendly way.

This void has been filled by plugin Advanced Custom Fields. Whenever we can control the environment where the site runs (such as when building a website for a client), we can install ACF and depend on it for creating all the relationships across entities.

However, that is not the case when submitting a theme or plugin to the WordPress directory. Since we don’t know in advance if the user installing the theme or plugin will also have ACF installed, we must then find a different way to solve the problem.

Luckily, Gutenberg can replace ACF (even though is not as simple, since it requires plenty of coding) by attaching custom blocks to the editor to let the user define the relationships across entities. For instance, the custom block can fetch the list of database entities that can be referenced using the WP REST API, display them on a select input, and save the selected entries through a block attribute.

Gutenberg will store the data within the post content, and we can retrieve it using function parse_blocks, as we explained on article Exposing WordPress site data for mobile apps.

This custom Gutenberg block makes sense only when editing the intended custom post type, so we need to remove it everywhere else. In this article, we will explore how to achieve this. We will first analyze the official solution, which is based on JavaScript, and then an alternative (and better) approach using PHP.

The official (unsatisfying) solution: Unregistering blocks through JavaScript code

The official solution to this problem is to unregister the block by executing JavaScript function wp.blocks.unregisterBlockType:

wp.domReady( function() {
  wp.blocks.unregisterBlockType( 'my-namespace/my-custom-block' );
} );

Please notice that there is also an alternative proposed approach, which is to use a whitelist:

var allowedBlocks = [
  'core/paragraph',
  'core/image',
  'core/html',
  'core/freeform'
];
 
wp.blocks.getBlockTypes().forEach( function( blockType ) {
  if ( allowedBlocks.indexOf( blockType.name ) === -1 ) {
    wp.blocks.unregisterBlockType( blockType.name );
  }
} );

However, this approach will not work for our situation, because we only know which is the block type that is forbidden from all other custom post types, instead of knowing which are all the allowed block types. Sure, we could calculate the whitelist by removing the blacklisted block type from the list of all block types, but then we’d rather already use the simpler blacklisting approach.

The blacklisting approach works, however I find this solution quite unsatisfactory, because of several reasons:

  • First registering the block in PHP as to then unregister it in JavaScript is not very sensible
  • The name of the post type is defined in PHP and referenced in JavaScript, so the code is not DRY (Don’t Repeat Yourself), which can potentially create bugs down the road if the developer is not careful and renames the post type in one place only
  • Even though it won’t be executed, the script for the blacklisted block type is still loaded, affecting performance
  • JavaScript requires extra steps (compilation, minifying, bundling, etc) over PHP, meaning more complexity and extra bureaucracy

Resolving this problem in PHP, if possible, shold lead to a neater solution. Let’s explore how to do this.

First (failed) approach with PHP: Using hook "allowed_block_types"

WordPress provides filter "allowed_block_types" to modify what block types are allowed for a post.

Whitelisting blocks can be easily accomplished, like this:

function wpdocs_allowed_block_types( $allowed_block_types, $post ) {
  if ( $post->post_type !== 'post' ) {
    return $allowed_block_types;
  }

  return array( 'core/paragraph' );
}
add_filter( 'allowed_block_types', 'wpdocs_allowed_block_types', 10, 2 );

Unfortunately, this filter does not work for the blacklisting approach, which is what we need. This is because the filter must return the whitelisted list of block types, to be calculated as all block types minus the blacklisted ones. However, this filter initially does not receive the array with all registered block types, but a true boolean value. Hence, it must first obtain the array of all blocks from somewhere in the system.

Yet, this information is not available on the PHP-side of the admin panel! There is function get_all_registered() from WP_Block_Type_Registry, however it returns only blocks registered through register_block_types, and many core blocks (such as "core/paragraph") are not registered this way. Then, we would need to discover the list of all core blocks not registered with register_block_types and manually add them to the list, which leads to plenty of bureaucracy (such as having to watch future releases of WordPress to discover new unregistered core blocks and add them to the list), and bugs are bound to happen.

So this approach doesn’t work. What else can we do?

Second (successful) approach with PHP: Register a block type only if the custom post type in editor is the right one

The WordPress editor knows which is the post type of the post being edited. Then, the solution is initially simple:

  • In the editor, check which is the post type of the post being created or edited
  • If it is the right CPT, only then register the block

Unfortunately, there is no function similar to is_singular($post_type) for the admin panel, as to determine the post type of the object being edited. And even though the post type information is stored under global variable $typenow, this variable is set late in the process, and not before hook "init" is executed (which is where we normally place the logic to register the block scripts), so we can’t use this variable for our purpose.

To solve this issue, we can recreate the logic to calculate the value for global variable $typenow. The steps to follow are:

  1. Check if we are on the admin panel
  2. Get the value of global variable $pagenow (which has been set by the time the "init" hook is invoked)
  3. Obtain the post type like this:
    • If the value of $pagenow is "post-new.php", then the post type of the new post is indicated under URL parameter 'post_type'
    • If instead it is "post.php", then the post type can be deduced from the object being edited
  4. If this is the intended custom post type, only then execute register_block_type

Here is the code:

add_action('init', 'maybe_register_custom_block');
function maybe_register_custom_block()
{
  // Check if this is the intended custom post type
  if (is_admin()) {
    global $pagenow;
    $typenow = '';
    if ( 'post-new.php' === $pagenow ) {
      if ( isset( $_REQUEST['post_type'] ) && post_type_exists( $_REQUEST['post_type'] ) ) {
        $typenow = $_REQUEST['post_type'];
      };
    } elseif ( 'post.php' === $pagenow ) {
      if ( isset( $_GET['post'] ) && isset( $_POST['post_ID'] ) && (int) $_GET['post'] !== (int) $_POST['post_ID'] ) {
        // Do nothing
      } elseif ( isset( $_GET['post'] ) ) {
        $post_id = (int) $_GET['post'];
      } elseif ( isset( $_POST['post_ID'] ) ) {
        $post_id = (int) $_POST['post_ID'];
      }
      if ( $post_id ) {
        $post = get_post( $post_id );
        $typenow = $post->post_type;
      }
    }
    if ($typenow != 'my-custom-post-type') {
      return;
    }
  }

  // Register the block
  $asset_file = include( plugin_dir_path( __FILE__ ) . 'build/index.asset.php');
  wp_register_script(
    'my-custom-block',
    plugins_url( 'build/block.js', __FILE__ ),
    $asset_file['dependencies'],
    $asset_file['version']
  );
  register_block_type( 'my-namespace/my-custom-block-name', array(
    'editor_script' => 'my-custom-block',
  ) );
}

This solution is a bit hacky, but it works well.

Conclusion

Registering plenty of blocks in the Gutenberg editor can bog down the user experience. Then, it is a good practice to avoid registering blocks whenever they are not needed, such as when creating a custom block that needs be accessed for a specific custom post type only.

The official way to do it, based on JavaScript code, is unsatisfactory, because it loads the unwanted scripts so performance still takes a hit, requires extra effort, and can lead to bugs.

In this article we learnt that we can achieve it with PHP code, by deciding if to register the block type or not depending on the current post type in the editor. This method is more effective: performance improves since scripts are never loaded, and it is simpler to execute, overall making the application be more resilient.

Related Posts

About Leonardo Losoviz

Leonardo Losoviz is an open source developer and technical writer, author of GraphQL by PoP, a CMS-agnostic GraphQL server in PHP. Find him on his blog leoloso.com and on Twitter @losoviz.

Reader Interactions

Droppin' design bombs every week!  5,751 subscriber so far!

You have successfully joined our subscriber list.

1 Comment

  1. Hans Schuijff
    3 years ago

    I’d been looking for a way to solve this. In my own test I found the condition “post_type_exists( $_REQUEST[‘post_type’] )” failed at first.

    Plugins may use the init hook to register_post_type too and the order of callback execution is than an issue. In my case it was the events calendar that made it fail. Easy to solve, of course, by adding a higher prio, but one needs to think of it.

    Perhaps the “post_type_exists( $_REQUEST[‘post_type’] )” condition also isn’t that important, since it will only be used to match my cpt in order to decide if the block is allowed.

    Not sure when the post_id query var is used (legacy cases?), but the code works for me like this, so thanks for sharing this solution.

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

*

*

Primary Sidebar

Subscribe

Join our 5000+ subscribers & get access to freebies, new tools, and much more!

You have successfully joined our subscriber list.

Useful Guides

What is WordPress?
How To Make A Website – Step by Step Beginner’s Guide
9 Best WordPress Hosting Compared (December 2024)
8 Best Backup WordPress Plugins Compared – 2023
14 Ways to Secure Your WordPress Site – Step by Step
Top 9 Email List Building Plugins for WordPress Compared (2023)
Build Your Tribe: 6 Top WordPress Membership Plugins Compared for 2024
11 Best Website Builders of 2022: Wix, Squarespace, Weebly, & More
8 Best WordPress Contact Form Plugins for 2024
How to Use Facebook Debugger and Open Graph to Fix Posting Issues
Top 10 Free Website Speed Test Tools for 2024
5 Top WordPress Landing Page Plugins Compared (2024)
5 Best WordPress Learning Management Systems (LMS) Plugins Compared – 2022
20 Best Google Fonts & How To Use Them
7 of the Best FTP Clients for Mac & Windows
11 Dropbox Alternatives to Securely Store Your Files in the Cloud
25 of the Useful and Best Brackets Extensions
What is Loremp Ispum? 18 Plain & Hysterical Lorem Ipsum Generators for 2024
How to Clear Browser Cache (Google Chrome, Firefox, Safari, Opera, Microsoft Edge, & Internet Explorer)
6 Best Managed WordPress Hosting Options for 2024

Latest Deals

  • Elegant Themes: 20% OFF on the best drag & drop theme & plugin
  • WPEngine Coupon: Get 20% off the best Managed WP Hosting
  • WPX Coupon: Get up to 50% off on one of the best hosting providers
  • Inmotion Coupon: 47% off + Free Domain on Inmotion hostnig
  • View More Deals  

Categories

  • Adobe Photoshop15
  • Coding19
  • Design36
  • Fonts28
  • Freebies3
  • Inspiration52
  • News6
  • Resources58
  • Showcase14
  • Tutorials6
  • WordPress Plugins29
  • WordPress Themes27
  • WordPress Tutorials27
  • WP Hosting13

DesignBombs content is free, which means that if you click on some of our referral links, we may earn a small commission. Learn more!

Home About WordPress Hosting FTC Disclosure Privacy Policy Contact

© 2008-2025 All Rights Reserved.