使用 Query Loop 块变体构建书评网格
WordPress 6.1 引入了扩展 Query Loop 块的新方法。这是一个重要的里程碑,因为它以极低的代码成本为插件开发者提供了大量功能。开发者无需构建自定义块来查询文章,只需过滤 WordPress 核心中的现有功能即可。
Query Loop 块是使用区块构建网站的主力。它是显示文章、页面和其他自定义文章类型内容的基础。
在 WordPress 6.1 之前,与 PHP 对应的 WP_Query 相比,它的主要用例仅限于显示以前可能实现的功能子集。这意味着开发者,尤其是在处理自定义文章类型的数据时,通常需要自己构建完整的自定义块。
如今,开发者可以在坚实的基础上进行构建,只需最少的代码即可输出几乎任何类型的内容。6.1 版本的更改允许插件作者跳过区块开发环节,直接扩展内置的 Query Loop 块。
其使用方式的一些例子包括:
- 按价格元字段显示产品网格。
- 按位置列出企业的商业目录。
- 点对点筹款活动的排行榜。
- 按评分输出书评。
在本教程中,你将学习如何处理列表中的最后一项:列出书评文章。你将从头到尾构建一个 WordPress 插件。结果将是一个类似于下图的 Query Loop 块变体:

这个简单教程中的基本方法也可以应用于更复杂的项目。
要求
除了一些基础的 JavaScript 开发知识和区块开发经验外,你的机器上应具备以下工具:
- Node/NPM 开发工具
- WordPress 开发站点
- 代码编辑器
有关设置这些工具的更多信息,请访问区块编辑器手册中的开发环境指南。
设置内容
在本教程中,假设你有一个客户,他喜欢时不时地写书评,并希望在其网站的不同位置(例如自定义页面)展示最新的书评。该客户有一个名为“书评”的自定义分类,并且已经写了几篇文章。
在你的开发环境中重现此场景。

首先,添加一个新的“书评”分类,并记下分类 ID。稍后你会需要它。然后,创建至少三篇分配给此分类的示例文章,并为每篇文章设置特色图片。
插件设置
在你的 wp-content/plugins 目录中创建一个新插件。将其命名为类似 book-reviews-grid 的名称(确切名称并不特别重要)。现在,按照以下特定结构添加文件:
book-reviews-grid
/index.php
/package.json
/src
/index.js
你可以将 index.php 更改为你想要的任何名称。它是你的插件的主要 PHP 文件。
PHP 设置
在你的插件主 PHP 文件中,添加包含一些基本信息的插件头部:
<?php
/**
* Plugin Name: Book Reviews Grid
* Version: 1.0.0
* Requires at least: 6.1
* Requires PHP: 7.4
*/
// Additional code goes here…
这将是本教程中你唯一需要的 PHP 文件,所有 PHP 代码都将放入其中。
构建过程设置
首先,打开你的 package.json 文件并添加 start 脚本。这将用于构建过程。如果需要,你可以添加其他字段,例如 name 和 description。
{
"scripts": {
"start": "wp-scripts start"
}
}
本教程需要 @wordpress/scripts 包,可以通过命令行安装:
npm install @wordpress/scripts --save-dev
设置好所有内容后,在命令行程序中输入以下内容:
npm run start
除了必需的 start 命令外,你还可以通过 @wordpress/scripts 包找到所有可用的脚本,并根据需要将它们添加到你的 package.json 中。
构建一个简单的 Query Loop 变体
注册一个简单的 Query Loop 变体(没有任何自定义查询变量集成)的过程只需要几十行代码。你必须导入 registerBlockVariation 并使用它来注册变体。
JavaScript:构建变体
在你的 src/index.js 文件顶部,添加以下代码行:
import { registerBlockVariation } from '@wordpress/blocks';
现在,你需要两条信息。首先,为变体决定一个唯一的名称。暂时使用 book-reviews。需要的第二条数据是你之前在本教程中创建的“书评”分类的 ID。
将这两个值分配给常量,如下面的代码片段所示:
const VARIATION_NAME = 'book-reviews';
const REVIEW_CATEGORY_ID = 8; // 分配自定义分类 ID。
现在,是时候注册变体了。首先,添加一些基本属性,例如名称、标题等,如下面的代码块所示:
registerBlockVariation( 'core/query', {
name: VARIATION_NAME,
title: 'Book Reviews',
icon: 'book',
description: 'Displays a list of book reviews.',
isActive: [ 'namespace' ],
// 其他变体选项...
} );
为 core/query 块注册变体时,需要设置两个必要的选项:
name属性应与你的唯一变体名称匹配。isActive属性应是一个包含namespace属性的数组(你将在下一步中定义此属性)。
从这一点来看,变体大部分是可定制的,但让我们一步一步来,为变体添加新选项。接下来要构建的是变体的属性。属性可以匹配 Query Loop 块接受的任何属性。
一个额外的必需属性是 namespace。它必须与变体名称匹配,以便 WordPress 能够检查它是否是活动变体。
在本教程中,变体显示“书评”分类中最新六篇文章。它还具有三列网格的宽布局。请随意根据你的喜好自定义选项。
registerBlockVariation( 'core/query', {
// ...之前的变体选项。
attributes: {
namespace: VARIATION_NAME,
query: {
postType: 'post',
perPage: 6,
offset: 0,
taxQuery: {
category: [ REVIEW_CATEGORY_ID ]
}
},
align: 'wide',
displayLayout: {
type: 'flex',
columns: 3
}
},
// 其他变体选项...
} );
开发者还可以通过设置 allowedControls 数组来选择显示哪些默认的 WordPress 控件(默认情况下显示所有控件)。这些在界面中显示为区块选项。有关控件及其定义的完整列表,请访问区块编辑器手册中的允许的控件部分。
以下示例添加了 order 和 author 控件:
registerBlockVariation( 'core/query', {
// ...之前的变体选项。
allowedControls: [
'order',
'author'
],
// 其他变体选项...
} );
最后,你应该为变体添加一些内部块。第一个顶级块应始终是 core/post-template。以下代码片段使用了核心的“文章特色图片”和“文章标题”块,没有进行自定义,但你可以随意添加其他块并为每个块设置默认选项。
registerBlockVariation( 'core/query', {
// ...之前的变体选项。
innerBlocks: [
[
'core/post-template',
{},
[
[ 'core/post-featured-image' ],
[ 'core/post-title' ]
],
]
]
} );
有关可用选项的完整概述,请访问以下资源:
PHP:加载 JavaScript
处理完基础的 JavaScript 后,现在你必须加载 JavaScript 文件本身。构建过程将生成两个文件:
- build/index.js: 要加载的 JavaScript 文件。
- build/index.asset.php: 脚本的依赖项数组和版本号。
以下代码片段应添加到 index.php 中。如果生成的资源文件存在,则获取其数据并加载编辑器中的脚本:
add_action( 'enqueue_block_editor_assets', 'myplugin_assets' );
function myplugin_assets() {
// 获取插件目录和 URL 路径。
$path = untrailingslashit( __DIR__ );
$url = untrailingslashit( plugins_url( '', __FILE__ ) );
// 获取自动生成的资源文件。
$asset_file = "{$path}/build/index.asset.php";
// 如果资源文件存在,获取其数据并加载脚本。
if ( file_exists( $asset_file ) ) {
$asset = include $asset_file;
wp_enqueue_script(
'book-reviews-variation',
"{$url}/build/index.js",
$asset['dependencies'],
$asset['version'],
true
);
}
}
有了这段代码,你应该能够通过区块插入器或区块编辑器中的斜杠命令(例如 /book reviews)添加“书评”变体:

如果你的变体不需要对查询的文章进行超出核心 Query Loop 块开箱即用功能的自定义,你可以在此处停止本教程。
将文章元数据集成到变体中
现在,让我们深入探讨一个基于现有代码构建的稍微高级的场景。你将构建一个控件,供用户根据文章元键和值对来显示书评。
实现此功能的关键在于 WordPress 提供的过滤器钩子。一旦你学会了如何使用这些钩子,就可以将它们扩展到自定义文章类型和其他实际项目中。
设置文章元数据
你需要一个值为 1 到 5 之间的 rating 文章元键,并将其附加到“书评”分类中的一篇或多篇文章上。最简单的方法是使用文章编辑屏幕上的自定义字段面板。
注意:如果看不到“自定义字段”面板,可以在编辑器的选项(⋮ 图标)> 偏好设置 > 面板菜单中启用它。
返回你的每篇书评文章,并添加一个评分值,如下图所示:

在实际项目中,你可能会希望为最终用户构建适当的表单字段,以便轻松选择星级评分。然而,这超出了本教程的范围。
JavaScript:添加区块变体控件
要为自定义 Query Loop 变体添加额外的控件,你需要在脚本中导入一些额外的模块。将以下代码添加到 src/index.js 文件的顶部。在构建其余功能时,你将使用这些模块。
// ...之前的导入。
import { addFilter } from '@wordpress/hooks';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl } from '@wordpress/components';
接下来,构建一个快速的辅助函数,用于根据区块的属性判断它是否与你的变体匹配。稍后你将在代码中使用它。大部分工作已经完成,因为你之前设置了变体名称常量和用于检查的命名空间。
const isBookReviewsVariation = ( props ) => {
const {
attributes: { namespace }
} = props;
return namespace && namespace === VARIATION_NAME;
};
现在,是时候构建一个用于显示自定义区块面板部分的组件了。稍后你可以在此处添加多个字段。目前,它将包含一个用于选择星级评分的表单字段。
以下代码使用 SelectControl 组件创建每个可用评分的下拉选择。这同样可以是一个单选列表、按钮组或完全自定义的 React 组件。这取决于你。
这段代码的关键部分是将星级评分值保存到 props.attributes.query.starRating。稍后你将需要它来修改文章查询。
const BookReviewControls = ( { props: {
attributes,
setAttributes
} } ) => {
const { query } = attributes;
return (
<PanelBody title="Book Review">
<SelectControl
label="Rating"
value={ query.starRating }
options={ [
{ value: '', label: '' },
{ value: 1, label: "1 Star" },
{ value: 2, label: "2 Stars" },
{ value: 3, label: "3 Stars" },
{ value: 4, label: "4 Stars" },
{ value: 5, label: "5 Stars" }
] }
onChange={ ( value ) => {
setAttributes( {
query: {
...query,
starRating: value
}
} );
} }
/>
</PanelBody>
);
};
构建好自定义面板部分和控件后,你必须过滤 Query Loop (core/query) 块以添加自定义控件。这就是之前 isBookReviewsVariation 辅助函数的用武之地。你将传递区块的属性给它,以判断它是否是自定义变体。如果匹配,则添加你的控件。
export const withBookReviewControls = ( BlockEdit ) => ( props ) => {
return isBookReviewsVariation( props ) ? (
<>
<BlockEdit {...props} />
<InspectorControls>
<BookReviewControls props={props} />
</InspectorControls>
</>
) : (
<BlockEdit {...props} />
);
};
addFilter( 'editor.BlockEdit', 'core/query', withBookReviewControls );
此时,当你的变体被使用时,你应该能看到一个标题为“Book Review”的部分,其中包含一个“Rating”选择下拉菜单,如下图所示:

此时选择评分不应改变查询的文章。还需要添加几个过滤器才能使其工作。
PHP:过滤查询的文章
你必须添加两个 PHP 过滤器,以便在编辑器和前端都能工作。然后,你将拥有一个功能齐全的、集成了文章元数据的 Query Loop 变体。
第一个过滤器将挂在 rest_{$post_type}_query 钩子上。因为你正在使用“post”文章类型构建此功能,所以钩子名称变为 rest_post_query。
此过滤器将针对该类型的每个查询运行,因此在更改之前需要检查自定义查询参数 (starRating)。你可以通过回调函数的 $request 参数来检查,该参数提供了 WP_REST_Request 类的实例。使用其 get_param() 方法来检查自定义查询参数。
如果设置了 starRating 值,你只需要将元键和值作为查询参数传递回去。为此,将以下代码添加到你的插件主 PHP 文件中:
add_filter( 'rest_post_query', 'myplugin_rest_book_reviews', 10, 2 );
function myplugin_rest_book_reviews( $args, $request ) {
$rating = $request->get_param( 'starRating' );
if ( $rating ) {
$args['meta_key'] = 'rating';
$args['meta_value'] = absint( $rating );
}
return $args;
}
现在,在编辑器中测试你之前构建的评分控件。如下图所示,只有具有所选评分的文章被查询:

虽然这在编辑器中有效,但你还需要使用 pre_render_block 钩子在区块在前端渲染时运行一些自定义代码。然后,你需要使用匿名函数在回调内部嵌套第二个过滤器到 query_loop_block_query_vars 钩子上。这样做的原因是你需要访问已解析的区块属性。
如果听起来有点复杂,确实如此。理想情况下,未来会有一种稍微不那么复杂的方法来实现这一点。
add_filter( 'pre_render_block', 'myplugin_pre_render_block', 10, 2 );
function myplugin_pre_render_block( $pre_render, $parsed_block ) {
// 判断这是否是自定义区块变体。
if ( 'book-reviews' === $parsed_block['attrs']['namespace'] ) {
add_filter(
'query_loop_block_query_vars',
function( $query, $block ) use ( $parsed_block ) {
// 如果查询了评分,则添加评分元键/值对。
if ( $parsed_block['attrs']['query']['starRating'] ) {
$query['meta_key'] = 'rating';
$query['meta_value'] = absint( $parsed_block['attrs']['query']['starRating'] );
}
return $query;
},
10,
2
);
}
return $pre_render;
}
现在,你应该在网站前端看到与编辑器中相同的查询文章。
有了这个基础,你可以将其扩展到其他项目。本质上,你可以使用几个过滤器和自定义控件构建任何你需要的查询类型。虽然本教程看起来有点长,但总共包含不到 200 行代码,与构建功能齐全的区块相比,这是一个重大的胜利。