社区新闻

在编辑器中获取和设置块绑定值

查看官方原文 ↗ 发布于

WordPress 6.7 预计将于 2024 年 11 月 12 日发布,其最大特性之一是能够直接从编辑器本身将区块属性绑定到自定义字段。

但如果你是那些自 WordPress 6.5 以来就一直在注册自定义绑定源的开发者之一,你可能想知道 WordPress 是否会为你的自定义源提供同样的支持。

答案是肯定的。如果你愿意投入一点精力。

WordPress 6.7 将提供一个公共 API,用于在编辑器中操作连接到自定义绑定源的区块。至少是一个部分的 API,它为你提供了大量新功能来构建一些有趣的功能。

在本教程中,我将引导你完成如何使自定义绑定源的数据出现在编辑器中,并允许用户从连接的区块编辑该数据。

有关 WordPress 6.7 中所有区块绑定 API 更新的更多信息,请查看 Make Core 博客上的开发说明。

设置你的插件

首先,让我们搞定基础部分。在你的 /wp-content/plugins 目录中创建一个名为 devblog-editor-bindings 的新文件夹,包含以下文件和子文件夹:

  • /public
  • /resources
    • /js
      • /editor.js
  • /plugin.php
  • /package.json
  • /webpack.config.js

当然,你也可以在主题中做同样的事情,但请记住,你需要将代码示例中使用的任何插件特定函数更改为适合主题的函数。

像往常一样创建插件时,你必须设置主插件的文件头,以便 WordPress 将其识别为有效插件。继续将此代码添加到你的 plugin.php 文件中:

<?php
/**
 * Plugin Name:       Dev Blog Editor Bindings
 * Plugin URI:        https://developer.wordpress.org/news
 * Description:       Exploring the Block Bindings API in the editor.
 * Version:           1.0.0
 * Requires at least: 6.7
 * Requires PHP:      7.4
 * Author:            Your Name
 * Author URI:        https://developer.wordpres.org/news
 * Text Domain:       devblog
 */

// Custom code goes here.

现在打开 package.json 并添加 startbuild 脚本,它们将指向你之前创建的 /resources/public 文件夹:

{
	"name": "devblog-editor-bindings",
	"scripts": {
		"start": "wp-scripts start --webpack-src-dir=resources --output-path=public",
		"build": "wp-scripts build --webpack-src-dir=resources --output-path=public"
	}
}

然后打开计算机的命令行程序并键入此命令以安装 @wordpress/scriptspath 包:

npm install @wordpress/scripts path --save-dev

最后的设置步骤是扩展 WordPress 的 webpack 配置,以便它知道如何处理你的自定义 editor.js 文件。将此代码添加到你的 webpack.config.js 文件中:

// WordPress webpack config.
const defaultConfig = require( '@wordpress/scripts/config/webpack.config' );

// Utilities.
const path = require( 'path' );

// Add any a new entry point by extending the webpack config.
module.exports = {
	...defaultConfig,
	...{
		entry: {
			'js/editor': path.resolve( process.cwd(), 'resources/js', 'editor.js' )
		}
	}
};

基础打好后,让我们进入有趣的部分。

注册自定义区块绑定源

注册自定义区块绑定是熟悉领域,因此我不会详细介绍过程的每个细节。如果这是你第一次接触区块绑定 API,请先阅读这两篇入门教程:

  • 引入区块绑定,第 1 部分:连接自定义字段
  • 引入区块绑定,第 2 部分:使用自定义绑定源

随着本教程的进行,你将学习如何使用 JavaScript 获取和设置文章数据。但你需要做的第一件事是注册一个绑定,用于在前端处理此数据。将此代码添加到你的 plugin.php 文件中:

add_action( 'init', 'devblog_register_binding_sources' );

function devblog_register_binding_sources() {
	register_block_bindings_source( 'devblog/post-data', [
		'label'              => __( 'Post Data', 'devblog' ),
		'get_value_callback' => 'devblog_post_data_callback',
		'uses_context'       => [ 'postId' ],
	]);
}

在上面的代码中,get_value_callback 值设置为 devblog_post_data_callback。每当 WordPress 在前端找到连接到绑定源的区块属性时,就会触发此函数。

让我们保持相对简单,专注于自定义绑定源的三个数据片段:

  • 文章标题
  • 文章摘要
  • 文章固定链接

这些都是字符串,因此非常适合当前的区块绑定 API。将此回调函数添加到你的 plugin.php 文件中:

function devblog_post_data_callback( $args, $block, $name ) {
	if ( ! isset( $args['key'] ) ) {
		return null;
	}

	$post_id = $block->context['postId'] ?? get_the_ID();

	if ( 'title' === $args['key'] ) {
		return get_post_field( 'post_title', $post_id );
	} elseif ( 'excerpt' === $args['key'] ) {
		return get_post_field( 'post_excerpt', $post_id );
	} elseif ( 'permalink' === $args['key'] ) {
		return get_permalink( $post_id );
	}

	return null;
}

此函数在连接到区块属性时检索文章标题、摘要或固定链接的值。否则,它返回 null

添加区块标记

现在让我们确保自定义绑定源确实有效——如果初始实现是坏的,跳转到 JavaScript 没有任何好处。像往常一样,尽早并经常测试。

转到 WordPress 管理后台的 文章 > 写文章。在新文章屏幕上,单击 选项 按钮(右上角的 图标)并在下拉列表中切换到 代码编辑器 视图。然后添加此区块标记,然后再切换回 可视化编辑器

<!-- wp:heading {
	"placeholder":"Add post title",
	"metadata":{
		"bindings":{
			"content":{
				"source":"devblog/post-data",
				"args":{"key":"title"}
			}
		}
	}
} -->
<h2 class="wp-block-heading"></h2>
<!-- /wp:heading -->

<!-- wp:paragraph {
	"placeholder":"Add post excerpt",
	"metadata":{
		"bindings":{
			"content":{
				"source":"devblog/post-data",
				"args":{"key":"excerpt"}
			}
		}
	}
} -->
<p></p>
<!-- /wp:paragraph -->

<!-- wp:buttons -->
<div class="wp-block-buttons">
	<!-- wp:button {
		"metadata":{
			"bindings":{
				"url":{
					"source":"devblog/post-data",
					"args":{"key":"permalink"}
				}
			}
		}
	} -->
	<div class="wp-block-button"><a class="wp-block-button__link wp-element-button">Read post <span aria-hidden="true" class="wp-exclude-emoji"></span></a></div>
	<!-- /wp:button -->
</div>
<!-- /wp:buttons -->

基本上,你所做的是使用区块绑定 API 重新创建常见的 WordPress 区块:

  • 标题块的内容连接到文章标题。
  • 段落块的内容连接到文章摘要。
  • 按钮块的 URL 连接到文章固定链接。

你还会在侧边栏中看到 属性 面板显示连接。在 WordPress 6.7 中,你无法覆盖该面板中显示的标签,但这应该在未来的版本中改变。

虽然这本身可能看起来并不具有革命性,但它实际上代表了真正强大的东西。你可以获取 任何 数据并将其连接到基本元素,如标题、段落和按钮。

本教程使用现有的文章数据,因为它易于获取且是最简单的展示对象。但你可以做的事情没有限制。你可以连接到用户数据、站点选项,甚至第三方 API。发挥你的想象力,思考所有的可能性。

在继续之前,有一个小提示。你会注意到上面截图中的 文章数据 占位符。当 content 属性不可编辑时会出现此情况。但在标题和段落区块标记中,定义了 placeholder 属性以在编辑器中显示 添加文章标题添加文章摘要 文本。一旦属性变为可编辑,这些占位符将在编辑器中反映出来。

在编辑器中获取和设置值

现在是你可能阅读本教程的部分:使用 JavaScript 在编辑器中操作绑定值。

还记得你之前在 package.json 中定义的 start 脚本吗?继续通过输入此命令来运行它:

npm run start

这将在你对 editor.js 进行更改时构建你的 JavaScript。

加载你的 JavaScript

editor.js 文件中工作之前,必须先加载它。将此代码添加到你的 plugin.php 文件中:

add_action( 'enqueue_block_editor_assets', 'devblog_editor_assets' );

function devblog_editor_assets() {
	$dir = untrailingslashit( plugin_dir_path( __FILE__ ) );
	$url = untrailingslashit( plugin_dir_url(  __FILE__ ) );

	if ( file_exists( "{$dir}/public/js/editor.asset.php" )) {
		$asset = include "{$dir}/public/js/editor.asset.php";

		wp_enqueue_script(
			'devblog-editor-bindings',
			"{$url}/public/js/editor.js",
			$asset['dependencies'],
			$asset['version'],
			true
		);

		wp_set_script_translations( 'devblog-editor-bindings', 'devblog' );
	}
}

JavaScript 基础

现在打开你的 /resources/js/editor.js 文件。你将在本教程的其余部分中使用它。特别是,你将使用两个 WordPress 函数:

  • registerBlockBindingsSource: 在客户端注册自定义绑定源所必需。
  • __(): 用于国际化文本字符串。

继续通过在 editor.js 文件顶部添加此代码来导入它们:

import { registerBlockBindingsSource } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';

正如你之前应该记得的,自定义绑定源在前端输出文章标题、摘要和固定链接。想法是,你也应该在编辑器中支持这些值。

但是,让我们将它们分为两组:

  • 只读: 此组将是出现在编辑器内容中但用户无法编辑的值(固定链接)。
  • 可编辑: 此组将在编辑器内容中既可查看又可编辑(标题和摘要)。

考虑到这个结构,创建两个数组,分别包含只读和可编辑的值:

const readOnlyAttributes = [
	'permalink'
];

const editableAttributes = [
	'title',
	'excerpt'
];

当然也可以使固定链接可编辑,但我想展示你可以根据你的用例以不同方式处理值。

在客户端注册绑定源

WordPress 6.7 引入了一个用于在编辑器中注册区块绑定的新函数:registerBlockBindingsSource()。让我们看看你可以设置的属性:

registerBlockBindingsSource({
	name: '',
	label: '',
	usesContext: [],
	getValues( { select, clientId, context, bindings } ) {
		return {};
	},
	setValues( { select, clientId, dispatch, context, bindings } ) {
	},
	canUserEditValue( { select, context, args } ) {
		return false;
	}
});

你可以为函数的对象参数定义几个属性:

  • name: 你的区块绑定源的命名空间标识符(例如,devblog/post-data)。如果在服务器上注册,这必须在你的 PHP 和 JavaScript 中都匹配。
  • label: 绑定源的国际化标签。如果未在此处定义,将默认为服务器上定义的标签。
  • usesContext: 要传递给回调函数的上下文数组。默认情况下,这使用服务器上定义的上下文。如果也在此处定义,则上下文会合并。
  • getValues: 当为区块属性设置绑定时,用于检索值的回调函数。它必须返回一个对象,其中属性名称为键,绑定数据为值。
  • setValues: 一个回调函数,允许你在用户编辑时操作数据。仅在使数据可编辑时才需要。
  • canUserEditValue: 一个回调,用于确定用户是否可以编辑绑定的区块属性。如果定义,它必须返回一个布尔值,默认为 false

在你研究 registerBlockBindingsSource 时,请记住在区块标记级别的结构,特别是 bindings 对象:

<!-- wp:heading {
	"placeholder":"Add post title",
	"metadata":{
		"bindings":{
			"content":{
				"source":"devblog/post-data",
				"args":{"key":"title"}
			}
		}
	}
} -->
<h2 class="wp-block-heading"></h2>
<!-- /wp:heading -->

标记中定义的属性将与 JavaScript 端的属性匹配。

现在添加在客户端注册区块绑定源所需的最基本值,即名称和上下文:

registerBlockBindingsSource({
	name: 'devblog/post-data',
	usesContext: [ 'postType' ],
	// 在后续步骤中添加其他代码。
});

有几点需要注意:

  • name 值与你在 PHP 中设置的 devblog/post-data 值以及区块标记中的 source 匹配。这是绝对必须的。
  • usesContext 方法添加了 postType,这与 PHP 函数中设置的不匹配。没关系。JavaScript 和 PHP 数组的值会合并,因此你可以在 JavaScript 中访问 context.postIdcontext.postType(如果需要)。稍后你将使用它来限制为特定的文章类型。

在编辑器中显示值

对于几乎任何自定义绑定源,你最想做的事情就是在编辑器中显示该数据。如果你还记得之前的截图,有一段非常无用的文本(添加文章数据),它只是添加数据的占位符。

让我们改变这一点,以便用户在编辑器中看到真实的数据,当没有定义数据时,它首先看起来像这样(注意自定义占位符出现了):

WordPress 文章编辑器,带有空白的标题、段落和按钮区块,尚未自定义。标题和段落具有自定义占位符文本。

对于这个特定的例子,你需要传入 getValues() 回调的两个可用参数:

  • select: 你将使用它来访问 core/editor 存储的 getEditedPostAttribute() 选择器。这将允许你检索特定的文章数据。
  • bindings: 一个对象,包含使用你的自定义源为区块设置的所有属性名称和参数。

在你的 registerBlockBindingsSource() 函数内部,添加 getValues() 回调:

getValues( { select, bindings } ) {
	const values = {};

	for ( const [ attributeName, source ] of Object.entries( bindings ) ) {
		if (
			editableAttributes.includes( source.args.key )
			|| readOnlyAttributes.includes( source.args.key )
		) {
			values[ attributeName ] = select( 'core/editor' ).getEditedPostAttribute( source.args.key );
		}
	}

	return values;
},

getValues() 在编辑器中每当 WordPress 找到连接到自定义绑定源的区块属性时触发。这让你有机会操作该值。

上面的代码循环遍历区块的每个连接属性,并检索它绑定的文章数据。最重要的是,返回的 values 对象必须包含属性名称作为键,数据作为值,例如:

{
	attributeName:    "some-value",
	anotherAttribute: "another-value"
}

这样,如果你在文章编辑界面中添加标题、摘要或固定链接,这些值将自动出现在连接的区块中:

WordPress 文章编辑器,其中标题块显示文章标题,段落块显示摘要。

此时,你所做的只是确保值出现在内容区域。用户还不能直接编辑区块。

使连接的区块可编辑

有时你会希望允许用户从区块实例编辑数据。你将使用 setValues() 回调来做到这一点。

对于你的文章数据源,你需要访问传入 setValues() 回调的 bindings 参数以及 dispatch() 函数,你将使用它来调度 core/editor 存储的 editPost() 操作。本质上,你将捕获用户设置的任何值并使用新数据更新文章。

将此代码添加到你的 registerBlockBindingsSource() 函数调用中:

setValues( { dispatch, bindings } ) {
	const values = {};

	for ( const [ attributeName, source ] of Object.entries( bindings ) ) {
		values[ source.args.key ] = source.newValue;
	}

	if ( Object.keys( values ).length > 0 ) {
		dispatch( 'core/editor' ).editPost( values );
	}
},

请注意,source.newValue 是由 API 提供的,每当用户在编辑器中添加新值时都会传递。

还有最后一件事需要使其工作。你需要告诉 WordPress 特定值是否可以编辑。在这种特定情况下,用户正在编辑文章数据,因此你已经知道在以下两个条件下他们可以编辑它:

  • 文章类型是 post
  • 键存储在你之前定义的 editableAttributes 数组中。

将此代码添加到你的 registerBlockBindingsSource() 函数内部:

canUserEditValue( { context, args } ) {
	return  'post' === context.postType && editableAttributes.includes( args.key );
}

保存代码并刷新编辑器屏幕后,你现在应该能够完全编辑连接到文章标题和摘要的标题块和段落块。

仅仅触及表面

我完全意识到本教程没有展示最先进的用例。它使用了易于从文章编辑器检索的数据。这是故意的。目标是在没有任何额外复杂性的情况下展示编辑器 API 的工作原理。

所以,如果我可以建议的话,这里有一个小作业:打开一个新项目并开始探索。

我很想看到你围绕用户数据、术语元数据、站点选项甚至与第三方 API 集成构建了什么。基础已经存在。你只需要在其上构建新的有趣的解决方案。