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:
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:
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:
Check if we are on the admin panel
Get the value of global variable $pagenow (which has been set by the time the "init" hook is invoked)
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
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!
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.
That again was no use: he but got another smile and a friendly look of the sort he no longer wanted. I said I thought I could gallop if Harry could, and in a few minutes we were up with the ambulance. It had stopped. There were several men about it, including Sergeant Jim and Kendall, which two had come from Quinn, and having just been in the ambulance, at Ferry's side, were now remounting, both of them openly in tears. "Hello, Kendall." We have this great advantage in dealing with Plato—that his philosophical writings have come down to us entire, while the thinkers who preceded him are known only through fragments and second-hand reports. Nor is the difference merely accidental. Plato was the creator of speculative literature, properly so called: he was the first and also the greatest artist that ever clothed abstract thought in language of appropriate majesty and splendour; and it is probably to their beauty of form that we owe the preservation of his writings. Rather unfortunately, however, along with the genuine works of the master, a certain number of pieces have been handed down to us under his name, of which some are almost universally admitted to be spurious, while the authenticity of others is a question on which the best scholars are still divided. In the absence of any very cogent external evidence, an immense amount of industry and learning has been expended on this subject, and the arguments employed on both sides sometimes make us doubt whether the reasoning powers of philologists are better developed than, according to Plato, were those of mathematicians in his time. The176 two extreme positions are occupied by Grote, who accepts the whole Alexandrian canon, and Krohn, who admits nothing but the Republic;115 while much more serious critics, such as Schaarschmidt, reject along with a mass of worthless compositions several Dialogues almost equal in interest and importance to those whose authenticity has never been doubted. The great historian of Greece seems to have been rather undiscriminating both in his scepticism and in his belief; and the exclusive importance which he attributed to contemporary testimony, or to what passed for such with him, may have unduly biassed his judgment in both directions. As it happens, the authority of the canon is much weaker than Grote imagined; but even granting his extreme contention, our view of Plato’s philosophy would not be seriously affected by it, for the pieces which are rejected by all other critics have no speculative importance whatever. The case would be far different were we to agree with those who impugn the genuineness of the Parmenides, the Sophist, the Statesman, the Philêbus, and the Laws; for these compositions mark a new departure in Platonism amounting to a complete transformation of its fundamental principles, which indeed is one of the reasons why their authenticity has been denied. Apart, however, from the numerous evidences of Platonic authorship furnished by the Dialogues themselves, as well as by the indirect references to them in Aristotle’s writings, it seems utterly incredible that a thinker scarcely, if at all, inferior to the master himself—as the supposed imitator must assuredly have been—should have consented to let his reasonings pass current under a false name, and that, too, the name of one whose teaching he in some respects controverted; while there is a further difficulty in assuming that his existence could pass unnoticed at a period marked by intense literary and philosophical activity. Readers who177 wish for fuller information on the subject will find in Zeller’s pages a careful and lucid digest of the whole controversy leading to a moderately conservative conclusion. Others will doubtless be content to accept Prof. Jowett’s verdict, that ‘on the whole not a sixteenth part of the writings which pass under the name of Plato, if we exclude the works rejected by the ancients themselves, can be fairly doubted by those who are willing to allow that a considerable change and growth may have taken place in his philosophy.’116 To which we may add that the Platonic dialogues, whether the work of one or more hands, and however widely differing among themselves, together represent a single phase of thought, and are appropriately studied as a connected series. Before entering on our task, one more difficulty remains to be noticed. Plato, although the greatest master of prose composition that ever lived, and for his time a remarkably voluminous author, cherished a strong dislike for books, and even affected to regret that the art of writing had ever been invented. A man, he said, might amuse himself by putting down his ideas on paper, and might even find written178 memoranda useful for private reference, but the only instruction worth speaking of was conveyed by oral communication, which made it possible for objections unforeseen by the teacher to be freely urged and answered.117 Such had been the method of Socrates, and such was doubtless the practice of Plato himself whenever it was possible for him to set forth his philosophy by word of mouth. It has been supposed, for this reason, that the great writer did not take his own books in earnest, and wished them to be regarded as no more than the elegant recreations of a leisure hour, while his deeper and more serious thoughts were reserved for lectures and conversations, of which, beyond a few allusions in Aristotle, every record has perished. That such, however, was not the case, may be easily shown. In the first place it is evident, from the extreme pains taken by Plato to throw his philosophical expositions into conversational form, that he did not despair of providing a literary substitute for spoken dialogue. Secondly, it is a strong confirmation of this theory that Aristotle, a personal friend and pupil of Plato during many years, should so frequently refer to the Dialogues as authoritative evidences of his master’s opinions on the most important topics. And, lastly, if it can be shown that the documents in question do actually embody a comprehensive and connected view of life and of the world, we shall feel satisfied that the oral teaching of Plato, had it been preserved, would not modify in any material degree the impression conveyed by his written compositions. breakfast in the kitchen by candle-light, and then drove the five The bargaining was interminable, something in this manner:— Then follows a long discussion in Hindi with the bystanders, who always escort a foreigner in a mob, ending in the question— There was a bright I. D. blanket spread on the ground a little way back from the fire, and she threw herself down upon it. All that was picturesque in his memories of history flashed back to Cairness, as he took his place beside Landor on the log and looked at her. Boadicea might have sat so in the depths of the Icenean forests, in the light of the torches of the Druids. So the Babylonian queen might have rested in the midst of her victorious armies, or she of Palmyra, after the lion hunt in the deserts of Syria. Her eyes, red lighted beneath the shadowing lashes, met his. Then she glanced away into the blackness of the pine forest, and calling her dog to lie down beside her, stroked its silky red head. The retreat was made, and the men found themselves again in the morning on the bleak, black heath of Drummossie, hungry and worn out, yet in expectation of a battle. There was yet time to do the only wise thing—retreat into the mountains, and depend upon a guerilla warfare, in which they would have the decided advantage. Lord George Murray now earnestly proposed this, but in vain. Sir Thomas Sheridan and other officers from France grew outrageous at that proposal, contending that they could easily beat the English, as they had done at Prestonpans and Falkirk—forgetting that the Highlanders then were full of vigour and spirit. Unfortunately, Charles listened to this foolish reasoning, and the fatal die was cast. "They said they were going for our breakfast," said Harry. "And I hope it's true, for I'm hungrier'n a rip-saw. But I could put off breakfast for awhile, if they'd only bring us our guns. I hope they'll be nice Springfield rifles that'll kill a man at a mile." "Dod durn it," blubbered Pete, "I ain't cryin' bekase Pm skeered. I'm cryin' bekase I'm afeared you'll lose me. I know durned well you'll lose me yit, with all this foolin' around." He came nearly every night. If she was not at the gate he would whistle a few bars of "Rio Bay," and she would steal out as soon as she could do so without rousing suspicion. Boarzell became theirs, their accomplice in some subtle, beautiful way. There was a little hollow on the western slope where they would crouch together and sniff the apricot scent of the gorse, which was ever afterwards to be the remembrancer of their love, and watch the farmhouse lights at Castweasel gleam and gutter beside Ramstile woods. "Yes, De Boteler," continued the lady, "I will write to him, and try to soothe his humour. You think it a humiliation—I would humble myself to the meanest serf that tills your land, could I learn the fate of my child. The abbot may have power to draw from this monk what he would conceal from us; I will at least make the experiment." The lady then, though much against De Boteler's wish, penned an epistle to the abbot, in which concession and apologies were made, and a strong invitation conveyed, that he would honour Sudley castle by his presence. The parchment was then folded, and dispatched to the abbot. "A very pretty method, truly! You know not the miners and forgers of Dean Forest!—why I would stake a noble to a silver-penny, that if you had discovered he was hidden there, and legally demanded him, he would be popped down in a bucket, to the bottom of some mine, where, even the art of Master Calverley could not have dragged him to the light of day until the Forest was clear of the pack:—but, however, to speak to the point," perceiving that the steward's patience was well nigh exhausted—"I saw Stephen Holgrave yesterday, in the Forest." HoME欧美一级 片a高清
ENTER NUMBET 0016juhohw.com.cn mdeykb.com.cn www.f71l5.net.cn gyetgc.com.cn www.langnest.com.cn www.lnchain.com.cn nlkxxz.com.cn www.two-l.net.cn www.muchone.com.cn titceb.com.cn
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.