## 补充资源

- [创建存储文章元数据的自定义区块](https://developer.wordpress.org/news/2023/03/03/creating-a-custom-block-that-stores-post-meta/)

### 第三步：使用文章元数据

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

#### 在 PHP 中使用文章元数据

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

```php
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' );
```

#### 在区块中使用文章元数据

您也可以在其他区块中使用文章元数据。在此示例中，数据会在每个段落区块渲染时（即显示给用户时）加载到其末尾。您可以根据需要将其替换为任何核心或自定义区块类型。

在 PHP 中，使用 [register_block_type](https://developer.wordpress.org/reference/functions/register_block_type/) 函数设置区块渲染时的回调，以包含元值。

```php
function myguten_render_paragraph( $block_attributes, $content ) {
	$value = get_post_meta( get_the_ID(), 'myguten_meta_block_field', true );
	// 输出前检查值是否已设置
	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',
) );
```

### 第四步：使用区块模板（可选）

使用元区块的一个问题是作者很容易忘记添加，因为它需要添加到每篇文章中。您可以通过使用[区块模板](/docs/reference-guides/block-api/block-templates.md)来解决这个问题。区块模板是按文章类型预定义的区块项列表。模板允许您为文章类型指定默认的初始状态。

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

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

```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` 参数：

```php
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`。

将元框转换为区块后，可以声明其存在以保持向后兼容性：

```php
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`](https://developer.wordpress.org/reference/functions/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 组件

组件渲染时会存储元数据框容器的引用，并从预取位置获取元数据框的 HTML 内容。

文章更新时，仅会提交处于活动状态的元数据框区域，以避免不必要的请求。元数据框提交不会创建额外修订版本。任何活动元数据框在触发 `REQUEST_POST_UPDATE` 时都会激活 Redux 操作（参见 `editor/effects.js`）。`REQUEST_META_BOX_UPDATES` 操作会将该元数据框状态设为 `isUpdating`。`isUpdating` 属性将传递至 `MetaBoxArea` 并触发表单提交。

当元数据框区域保存时，我们会显示更新遮罩层，防止用户在保存过程中修改表单值。

示例保存地址形如：

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

该地址通过 `_wpMetaBoxUrl` 全局变量自动传递给 React。

此页面模拟 `post.php` 的文章表单，提交时会触发所有常规钩子和操作，并具备正确的全局状态以正常执行所有 PHP 元数据框逻辑，无需修改现有代码。提交成功后，React 会触发 `handleMetaBoxReload` 来移除更新遮罩。

### 常见兼容性问题

大多数 PHP 元数据框在区块编辑器中应能继续工作，但包含高级功能的元数据框可能会出现异常。以下是元数据框在区块编辑器中可能无法正常工作的常见原因：

-   依赖针对旧编辑器文章标题、内容字段及其他元数据框选择器的插件
-   依赖 TinyMCE API 的插件（区块编辑器中不再存在单一 TinyMCE 实例）
-   在“提交”或“保存”时更新 DOM 的插件

另请注意：若插件触发输出 PHP 警告或通知，将导致 HTML 文档类型（`<!DOCTYPE html>`）输出异常，从而使浏览器启用“怪异模式”（当浏览器无法识别文档类型时激活的兼容模式）。区块编辑器不适用于此模式，但可能*看似*正常运行。若遇到*元数据框覆盖编辑器*等布局问题，请检查网页源代码确认文档类型声明是否为页面首行内容。JavaScript 控制台也会显示相关警告提示。

# 元数据框

## 概述

在区块编辑器出现之前，自定义元数据框被用于扩展编辑器功能。现在有了新的扩展方式，既赋予开发者更多能力，又为内容创作者带来更佳体验。建议将旧版自定义元数据框迁移至以下新方法之一，为编辑器用户创造更统一连贯的体验。

区块编辑器确实支持大多数现有元数据框，详见[下文向后兼容性说明](#向后兼容性)。

若您需要在编辑器外部处理文章元数据，请参阅[侧边栏教程](/docs/how-to-guides/sidebar-tutorial/plugin-sidebar-0.md)。

### 使用区块存储元数据

通常区块会将属性值存储在序列化的区块HTML中。但您也可以创建将属性值保存为文章元数据的区块，这样就能在模板的任何位置通过编程方式访问这些数据。

本指南将演示如何创建能够提示用户输入单个值，并将其保存为文章元数据的区块。

## 准备工作

本指南假设您已熟悉WordPress插件、文章元数据和基础JavaScript。建议先学习[JavaScript入门教程](/docs/getting-started/fundamentals/javascript-in-the-block-editor.md)打好基础。

虽然本指南将逐步创建基础区块，但推荐通过[创建区块教程](/docs/getting-started/tutorial.md)来更深入理解自定义区块的开发流程。

您需要准备：

-   WordPress开发环境
-   已激活并可编辑的基础插件
-   配置好构建和队列加载的JavaScript环境

您可参考[完整的元数据区块示例](https://github.com/WordPress/block-development-examples/tree/trunk/plugins/meta-block-bb1e55)来搭建开发环境。

## 分步指南

1.  [注册元字段](#步骤1-注册元字段)
2.  [添加元数据区块](#步骤2-添加元数据区块)
3.  [使用文章元数据](#步骤3-使用文章元数据)
4.  [收尾工作](#步骤4-使用区块模板-可选)

### 步骤1：注册元字段

文章元字段是用于存储文章扩展数据的WordPress对象。使用前需先注册新元字段。详见[文章元数据管理](https://developer.wordpress.org/plugins/metadata/managing-post-metadata/)文档。

注册字段时请注意`show_in_rest`参数，这能确保数据被包含在REST API中（区块编辑器通过该API加载和保存元数据）。更多信息请参阅[`register_post_meta`](https://developer.wordpress.org/reference/functions/register_post_meta/)函数说明。

此外，文章类型需支持`custom-fields`才能使`register_post_meta`函数正常工作。

将以下代码添加到PHP插件中以注册字段：

```php
<?php
// 注册自定义元标签字段
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`：

```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
					__nextHasNoMarginBottom
					__next40pxDefaultSize				
					label="元数据区块字段"
					value={ metaFieldValue }
					onChange={ updateMetaValue }
				/>
			</div>
		);
	},

	// 不保存信息至区块
	// 数据通过钩子保存至文章元数据
	save: () => {
		return null;
	},
} );
```

创建文章并添加元数据区块即可验证功能。您将看到可输入值的字段。当保存文章（草稿或已发布）时，文章元数据值也会同步保存。可通过保存后重新加载草稿来验证——重新加载后表单仍将保留输入内容。

您还可以检查数据库表`wp_postmeta`，确认新文章ID包含新字段数据。

**故障排除**：请确保在每次修改后重新构建代码（您更新了步骤1的PHP代码，且JavaScript文件已正确加入队列）。检查构建输出和开发者控制台是否有错误信息。