title: "元数据框" post_status: publish comment_status: open taxonomy: category: - gutenberg-docs post_tag: - How To Guides - Repos - Data


元数据框

概述

在区块编辑器出现之前,自定义元数据框被用于扩展编辑器功能。如今已有新的扩展方式,为开发者提供更强大的能力,并为内容创作者带来更优体验。建议将旧版自定义元数据框迁移至这些新方法之一,从而为编辑器使用者创造更统一、更一致的体验。

区块编辑器确实支持大多数现有元数据框,详见下文向后兼容性章节。

若您有意在编辑器外部处理文章元数据,请参阅侧边栏教程。

使用区块存储元数据

通常,区块将属性值存储在序列化的区块 HTML 中。但您也可以创建一个区块,将其属性值保存为文章元数据,以便在模板的任何位置通过编程方式访问。

本指南展示了如何创建一个提示用户输入单个值,并将其保存到文章元数据的区块。

开始之前

本指南假设您已熟悉 WordPress 插件、文章元数据和基础 JavaScript。建议先阅读 JavaScript 入门教程 了解基础知识。

本指南将引导您创建一个基础区块,但建议您先学习 创建区块教程 以更深入地理解自定义区块的开发。

您需要准备:

您可参考 完整的元数据区块示例 来配置您的开发环境。

分步指南

  1. 注册元字段
  2. 添加元区块
  3. 使用文章元数据
  4. 最终完善

Step 1: Register meta field

A post meta field is a WordPress object used to store extra data about a post. You need to first register a new meta field prior to use. See Managing Post Metadata to learn more about post meta.

When registering the field, note the show_in_rest parameter. This ensures the data will be included in the REST API, which the block editor uses to load and save meta data. See the register_post_meta function definition for extra information.

Additionally, your post type needs to support custom-fields for register_post_meta function to work

To register the field, add the following to your PHP plugin:

<?php
// register custom meta tag field
function myguten_register_post_meta() {
    register_post_meta( 'post', 'myguten_meta_block_field', array(
        'show_in_rest' => true,
        'single' => true,
        'type' => 'string',
    ) );
}
add_action( 'init', 'myguten_register_post_meta' );

步骤 2:添加元数据块

通过上一步注册的元字段,接下来创建一个新块来向用户显示字段值。

块可以使用 useEntityProp 钩子来获取或更改元值。

将此代码添加到 JavaScript src/index.js 文件中:

import { registerBlockType } from '@wordpress/blocks';
import { TextControl } from '@wordpress/components';
import { useSelect } from '@wordpress/data';
import { useEntityProp } from '@wordpress/core-data';
import { useBlockProps } from '@wordpress/block-editor';

registerBlockType( 'myguten/meta-block', {
    edit: ( { setAttributes, attributes } ) => {
        const blockProps = useBlockProps();
        const postType = useSelect(
            ( select ) => select( 'core/editor' ).getCurrentPostType(),
            []
        );

        const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' );

        const metaFieldValue = meta[ 'myguten_meta_block_field' ];
        const updateMetaValue = ( newValue ) => {
            setMeta( { ...meta, myguten_meta_block_field: newValue } );
        };

        return (
            <div { ...blockProps }>
                <TextControl
                    __next40pxDefaultSize               
                    label="Meta Block Field"
                    value={ metaFieldValue }
                    onChange={ updateMetaValue }
                />
            </div>
        );
    },

    // 没有信息保存到块中。
    // 数据通过钩子保存到文章元数据。
    save: () => {
        return null;
    },
} );

通过创建一篇文章并添加元数据块来确认其正常工作。您将看到可以输入值的字段。当您保存文章(无论是草稿还是已发布)时,文章元值也会被保存。您可以通过保存并重新加载草稿来验证,重新加载后表单仍将保留填写的内容。

您还可以通过检查数据库表 wp_postmeta 来确认数据已保存,并确认新文章 ID 包含新字段数据。

故障排除:请确保在更改之间构建代码,您已更新了步骤 1 中的 PHP 代码,并且 JavaScript 文件已正确入队。检查构建输出和开发者控制台是否有错误。

第三步:使用文章元数据

您可以通过多种方式使用上一步存储的文章元数据。

在 PHP 中使用文章元数据

第一个示例使用文章元字段的值,并将其包装在 H4 标签中附加到文章内容的末尾。

function myguten_content_filter( $content ) {
    $value = get_post_meta( get_the_ID(), 'myguten_meta_block_field', true );
    if ( $value ) {
        return sprintf( "%s <h4> %s </h4>", $content, esc_html( $value ) );
    } else {
        return $content;
    }
}
add_filter( 'the_content', 'myguten_content_filter' );

Use post meta in a block

You can also use the post meta data in other blocks. For this example the data is loaded at the end of every Paragraph block when it is rendered, ie. shown to the user. You can replace this for any core or custom block types as needed.

In PHP, use the register_block_type function to set a callback when the block is rendered to include the meta value.

function myguten_render_paragraph( $block_attributes, $content ) {
    $value = get_post_meta( get_the_ID(), 'myguten_meta_block_field', true );
    // check value is set before outputting
    if ( $value ) {
        return sprintf( "%s (%s)", $content, esc_html( $value ) );
    } else {
        return $content;
    }
}

register_block_type( 'core/paragraph', array(
    'api_version' => 3,
    'render_callback' => 'myguten_render_paragraph',
) );

步骤 4:使用区块模板(可选)

使用元区块的一个问题是作者很容易忘记添加它,因为它需要被添加到每篇文章中。您可以通过使用区块模板来解决这个问题。区块模板是每种文章类型的预定义区块列表。模板允许您为文章类型指定默认的初始状态。

在此示例中,您将使用模板在文章顶部自动插入元区块。

将以下代码添加到 myguten-meta-block.php 文件中:

function myguten_register_template() {
    $post_type_object = get_post_type_object( 'post' );
    $post_type_object->template = array(
        array( 'myguten/meta-block' ),
    );
}
add_action( 'init', 'myguten_register_template' );

您还可以在数组中添加其他区块类型,包括占位符,甚至可以将文章锁定为一组特定的区块。模板是控制编辑体验的强大工具,更多信息请参阅上面链接的文档。

结论

本指南展示了如何使用区块读写文章元数据。关于与现有元框的向后兼容性,请参阅以下章节。

向后兼容性

测试、转换和维护现有的元框

在将元框转换为区块之前,先测试元框是否能在区块编辑器中正常工作,并明确标记其兼容性,可能会更容易。

如果某个元框无法在区块编辑器中工作,且无法更新以使其正常工作,下一步是在元框声明中添加 __block_editor_compatible_meta_box 参数:

add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback',
    null, 'normal', 'high',
    array(
        '__block_editor_compatible_meta_box' => false,
    )
);

WordPress 将不会显示该元框,而是显示一条消息,说明它与区块编辑器不兼容,并包含一个指向经典编辑器插件的链接。默认情况下,__block_editor_compatible_meta_box 为 true。

将元框转换为区块后,可以将其声明为向后兼容的现有元框:

add_meta_box( 'my-meta-box', 'My Meta Box', 'my_meta_box_callback',
    null, 'normal', 'high',
    array(
        '__back_compat_meta_box' => true,
    )
);

当使用区块编辑器时,此元框将不再显示在元框区域,因为它现在仅用于向后兼容的目的。在经典编辑器中,它将像以前一样显示。

元框数据收集

在每次区块编辑器页面加载时,我们会注册一个用于收集元框数据的操作,以判断某个区域是否为空。收集元框数据后,原始全局状态将被重置。

参见 register_and_do_post_meta_boxes。

该操作会遍历 post.php 用于注册元框的函数和钩子,即 add_meta_boxes、add_meta_boxes_{$post->post_type} 和 do_meta_boxes。

元框会经过过滤,移除所有核心元框、标准自定义分类法元框,以及任何声明仅出于向后兼容目的而存在的元框。

然后检查此类特定元框的每个位置是否处于活动状态。若非空,则存储值 true;若为空,则存储值 false。此元框位置数据随后由编辑器 Redux 存储通过 INITIALIZE_META_BOX_STATE 进行分发。

理想情况下,这可以在编辑器实例化时完成,从而简化此流程。然而,在 admin_enqueue_scripts(我们在此调用 initializeEditor())之前无法获知元框状态。除非我们将 initializeEditor() 移至页脚或 admin_head 之后的某个时间点触发,否则目前只能如此处理。随着编辑器引导机制的最新变更,这可能现已可行。请使用 ACF 进行测试以确保无误。

Redux 与 React 元框管理

渲染区块编辑器时,元框会被渲染到隐藏的 #metaboxes 容器中。

默认情况下,Redux 存储会将所有元框标记为非活动状态。当接收到 INITIALIZE_META_BOX_STATE 时,存储会通过将 isActive 标志设为 true 来更新所有活动元框区域。随后 React 将检查 Redux 传递给 MetaBox 组件的新属性。如果该 MetaBox 已激活,则会渲染 MetaBoxArea 组件而非空值。MetaBox 组件是连接 MetaBoxArea 与 Redux 存储的容器组件。若无活动元框,则不执行任何操作。由于所有核心元框已被移除,这将成为默认行为。

MetaBoxArea 组件

组件渲染时会存储 metabox 容器的引用,并从预取位置获取 metabox 的 HTML 代码。

文章更新时,仅提交处于激活状态的 metabox 区域,避免不必要的请求。metabox 提交不会创建额外修订版本。任何激活的 metabox 都会在 REQUEST_POST_UPDATE 时触发 Redux 动作(参见 editor/effects.js)。REQUEST_META_BOX_UPDATES 动作会将对应 metabox 状态设为 isUpdating,该属性将传入 MetaBoxArea 并触发表单提交。

当 metabox 区域保存时,会显示更新覆盖层,防止用户在保存过程中修改表单值。

示例保存 URL 格式如下:

example.org/wp-admin/post.php?post=1&action=edit&meta-box-loader=1

该 URL 通过全局变量 _wpMetaBoxUrl 自动传入 React。

此页面模拟 post.php 文章表单,提交时将触发所有常规钩子和动作,并具备正确的全局状态以正常执行 PHP metabox 相关逻辑,无需修改现有代码。提交成功后,React 会触发 handleMetaBoxReload 来移除更新覆盖层。

常见兼容性问题

大多数 PHP 元框在区块编辑器中应能继续工作,但某些包含高级功能的元框可能会失效。以下是元框在区块编辑器中可能无法正常工作的常见原因:

请注意:如果您的插件触发 PHP 警告或通知输出到页面,将导致 HTML 文档类型(<!DOCTYPE html>)输出错误。这将使浏览器启用“怪异模式”进行渲染——当浏览器无法识别文档类型时会激活此兼容层。区块编辑器并非设计在此模式下运行,但表面可能看似正常。若遇到元框覆盖编辑器或其他布局问题,请检查文档的原始页面源代码,确保文档类型定义是页面最先输出的内容。JavaScript 控制台也会显示相关警告提示此问题。

其他资源