自制WordPress插件中InnerBlock无法保存问题求助
我是WordPress开发新手,正在开发一款包含自定义区块的插件,编辑器的HTML结构和样式用JS实现,前端渲染逻辑放在PHP文件中。
目前遇到的问题:开发了Hero区块,其中添加了InnerBlocks标签,允许插入GenericHeader自定义区块。编辑器中可以正常添加GenericHeader到Hero区块,保存操作看起来正常,但刷新页面后GenericHeader区块就消失了。附上相关代码寻求帮助:
Hero.js
import "./hero-block.scss" import {InnerBlocks} from "@wordpress/block-editor" // custom block type hero wp.blocks.registerBlockType("dotnyma-custom-blocks/hero", { title: "Hero", icon: "image", category: "common", attributes: { imageUrl: {type: "string"}, }, edit: EditHeroComponent, save: function () { return null; } }); function EditHeroComponent(props) { const {attributes, setAttributes} = props; return ( <> <div className="hero-editor-container"> <img src={attributes.imageUrl} alt='test'/> <div className="hero-editor-content-container"> <InnerBlocks allowedBlocks={['dotnyma-custom-blocks/generic-header']} /> </div> </div> </> ) }
hero.php
<?php class HeroBlock { function heroBlockRender($attributes, $content, $block_class): string { $item_count = count($block_class->inner_blocks); if ( $item_count < 1 ) { return '<h1>No blocks found</h1>'; } wp_enqueue_style('customBlockTypesStyles'); ob_start(); ?> <div class="hero-editor-container"> <img src="<?php echo esc_html($attributes['imageUrl'])?>" alt='test'/> <div class="hero-editor-content-container"> <?php // iterate over the available inner blocks for ($index = 1; $index <= $item_count; $index++) : // Get the inner block data $inner_block = $block_class->inner_blocks->current(); // Holds the inner block attribute $attribute = $inner_block->attributes; // This will display the attributes data var_dump($attribute); // increase the index in the WP_Block_List class used to retrieve the current block $block_class->inner_blocks->next(); endfor; // reset the index in the WP_Block_List class to the initial state $block_class->inner_blocks->rewind(); ?> </div> </div> <?php return ob_get_clean(); } } ?>
genericHeader.js
import "./generic-header.scss" import { RichText } from "@wordpress/block-editor"; // custom block type hero wp.blocks.registerBlockType("dotnyma-custom-blocks/generic-header", { title: "Generic header", icon: "image", category: "common", attributes: { text: { type : "string"}, size: { type : "string", default: "large" } }, edit: EditGenericHeaderComponent, save: function () { return null; } }); function EditGenericHeaderComponent(props) { const {attributes, setAttributes} = props; function handleTextChange(event) { console.log(event); setAttributes( {text: event} ); } return ( <> <RichText tagName={"h1"} value={attributes.text} onChange={handleTextChange}/> </> ) }
genericHeader.php
<?php class GenericHeaderBlock { function genericHeaderRender(): string { wp_enqueue_style('customBlockTypesStyles'); ob_start(); ?> <h1>this is working</h1> <?php return ob_get_clean(); } } ?>
Index.js
import "./index.scss" import "./blocks/hero/hero-block" import "./blocks/generic-header/generic-header"
index.php
<?php /* Plugin Name: **************** Description: ************** Version: 1.0 Author: *********** Author URI: *********** */ if (!defined('ABSPATH')) exit; include (__DIR__ . "/src/blocks/hero/hero-block.php"); include (__DIR__ . "/src/blocks/generic-header/generic-header.php"); class DotNymaCustomBlocks { function __construct() { add_action('init', array($this, 'customBlockAssets')); } function customBlockAssets(): void { $heroBlock = new HeroBlock(); $genericHeaderBlock = new GenericHeaderBlock(); wp_register_style('customBlockTypesStyles', plugin_dir_url(__FILE__) . 'build/index.css'); wp_register_script('customBlockTypes', plugin_dir_url(__FILE__) . 'build/index.js', array('wp-blocks', 'wp-element', 'wp-editor')); register_block_type('dotnyma-custom-blocks/hero', array( 'editor_script' => 'customBlockTypes', 'editor_style' => 'customBlockTypesStyles', 'render_callback' => [ $heroBlock, 'heroBlockRender' ] )); register_block_type('dotnyma-custom-blocks/generic-header', array( 'editor_script' => 'customBlockTypes', 'editor_style' => 'customBlockTypesStyles', 'render_callback' => [$genericHeaderBlock, 'genericHeaderRender'] )); } } $dotNymaCustomBlocks = new DotNymaCustomBlocks(); ?>
问题出在几个关键配置和渲染逻辑上,以下是修复步骤:
1. Hero.js 中保存InnerBlocks内容
对于包含InnerBlocks的动态区块,save函数不能返回null,必须返回InnerBlocks.Content来告诉编辑器保存内部区块的数据。修改Hero.js的save函数:
save: function () { return <InnerBlocks.Content />; }
2. Hero.php 中正确渲染子区块
原代码手动遍历WP_Block_List的方式错误,应该用foreach循环,并调用render()方法渲染子区块,而不是var_dump。修改hero.php的render函数:
function heroBlockRender($attributes, $content, $block): string { $item_count = count($block->inner_blocks); if ( $item_count < 1 ) { return '<h1>No blocks found</h1>'; } wp_enqueue_style('customBlockTypesStyles'); ob_start(); ?> <div class="hero-editor-container"> <img src="<?php echo esc_url($attributes['imageUrl'])?>" alt='test'/> <div class="hero-editor-content-container"> <?php foreach ($block->inner_blocks as $inner_block) { echo $inner_block->render(); } ?> </div> </div> <?php return ob_get_clean(); }
注:把参数名$block_class改成$block(符合WP规范),同时图片地址用esc_url替代esc_html更适配URL场景。
3. GenericHeader.php 接收并使用区块属性
原渲染函数没有接收区块属性,无法显示编辑器中输入的文本。修改genericHeader.php的render函数:
function genericHeaderRender($attributes): string { wp_enqueue_style('customBlockTypesStyles'); ob_start(); ?> <h1><?php echo esc_html($attributes['text'] ?? ''); ?></h1> <?php return ob_get_clean(); }
添加$attributes参数,渲染实际输入的文本,同时处理属性为空的边界情况。
4. 验证动态区块配置
两个区块已通过render_callback正确注册为动态区块,这部分index.php配置无需修改。
完成以上修改后,保存并刷新页面,GenericHeader区块就能正常显示了。
内容的提问来源于stack exchange,提问作者Mitchel Abrahams

