You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自制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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 13:37:04