## 补充资源
- [创建存储文章元数据的自定义区块](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
%s
", $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 文档类型(``)输出异常,从而使浏览器启用“怪异模式”(当浏览器无法识别文档类型时激活的兼容模式)。区块编辑器不适用于此模式,但可能*看似*正常运行。若遇到*元数据框覆盖编辑器*等布局问题,请检查网页源代码确认文档类型声明是否为页面首行内容。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
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 (
);
},
// 不保存信息至区块
// 数据通过钩子保存至文章元数据
save: () => {
return null;
},
} );
```
创建文章并添加元数据区块即可验证功能。您将看到可输入值的字段。当保存文章(草稿或已发布)时,文章元数据值也会同步保存。可通过保存后重新加载草稿来验证——重新加载后表单仍将保留输入内容。
您还可以检查数据库表`wp_postmeta`,确认新文章ID包含新字段数据。
**故障排除**:请确保在每次修改后重新构建代码(您更新了步骤1的PHP代码,且JavaScript文件已正确加入队列)。检查构建输出和开发者控制台是否有错误信息。