286 lines
14 KiB
Markdown
286 lines
14 KiB
Markdown
|
|
### 在前端实现自定义查询功能
|
|||
|
|
|
|||
|
|
查询循环区块主要通过文章模板区块运作,该区块接收属性并据此构建查询。查询循环区块的其他一级子元素(如分页区块)也以相同方式工作。它们构建查询后,通过 [`query_loop_block_query_vars`](https://developer.wordpress.org/reference/hooks/query_loop_block_query_vars/) 过滤器暴露结果。
|
|||
|
|
|
|||
|
|
您可以通过挂接该过滤器来修改查询。请务必确保不会对其他查询循环区块产生副作用,至少需要检查仅将过滤器应用于您的变体!
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
if( 'my-plugin/books-list' === $block[ 'attrs' ][ 'namespace' ] ) {
|
|||
|
|
add_filter(
|
|||
|
|
'query_loop_block_query_vars',
|
|||
|
|
function( $query ) {
|
|||
|
|
/** 在此处读取区块的自定义查询参数并构建查询 */
|
|||
|
|
},
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
(上述代码中,我们假设您有某种访问区块的方式,例如通过 [`pre_render_block`](https://developer.wordpress.org/reference/hooks/pre_render_block/) 过滤器,但具体解决方案可能因使用场景而异,因此这并非硬性建议)。
|
|||
|
|
|
|||
|
|
### 在编辑器端实现自定义查询功能
|
|||
|
|
|
|||
|
|
要完成自定义变体,我们可能希望编辑器能响应自定义查询的更改,并相应显示适当的预览。虽然这对区块功能并非必需,但能为区块使用者提供完全集成的用户体验。
|
|||
|
|
|
|||
|
|
查询循环区块通过 [WordPress REST API](https://developer.wordpress.org/rest-api/) 获取文章以显示预览。任何添加到 `query` 对象的额外参数都将作为查询参数传递给 API。这意味着这些额外参数要么需要 REST API 支持,要么需要通过自定义过滤器处理,例如 [`rest_{$this->post_type}_query`](https://developer.wordpress.org/reference/hooks/rest_this-post_type_query/) 过滤器,该过滤器允许您介入自定义文章类型的任何 API 请求。如下所示:
|
|||
|
|
|
|||
|
|
```php
|
|||
|
|
add_filter(
|
|||
|
|
'rest_book_query',
|
|||
|
|
function( $args, $request ) {
|
|||
|
|
/** 我们可以从这里访问自定义参数 */
|
|||
|
|
$book_author = $request->get_param( 'bookAuthor' );
|
|||
|
|
/** ...您的自定义查询逻辑 */
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
就这样,您就创建了一个功能完整的查询循环区块变体!
|
|||
|
|
|
|||
|
|
# 扩展查询循环区块
|
|||
|
|
|
|||
|
|
查询循环区块是一个功能强大的工具,允许用户循环遍历指定的文章列表,并显示一系列将继承列表中每篇文章上下文的区块。例如,可以将其设置为循环遍历特定分类下的所有文章,并为每篇文章显示其特色图像。当然,功能远不止于此!
|
|||
|
|
|
|||
|
|
但正因为查询循环区块功能强大且支持高度自定义,它也可能令人望而生畏。大多数用户不希望面对查询循环区块的全部功能,因为他们可能不熟悉“查询”概念及其相关技术术语。相反,大多数用户可能更青睐预设版本的区块,具有更少的设置项和更清晰的命名。默认提供的“文章列表”变体就是这种做法的良好示例:用户可以在不接触技术细节的情况下使用查询循环区块,同时也更有可能发现并理解该区块的用途。
|
|||
|
|
|
|||
|
|
同样地,许多扩展开发者可能需要提供定制化区块版本的方法,这些版本具有自己的预设配置、附加设置,并剔除与其使用场景无关的自定义选项(例如,通常针对其自定义文章类型)。查询循环区块提供了非常强大的方式来创建此类变体。
|
|||
|
|
|
|||
|
|
## 通过变体扩展区块
|
|||
|
|
|
|||
|
|
通过注册具有特定查询循环区块设置的自定义区块变体,您可以更精细地控制其呈现方式,同时仍能使用查询循环区块底层提供的全部功能。如果您不熟悉区块变体,可在此处了解更多信息。
|
|||
|
|
|
|||
|
|
通过区块变体API,您可以为您的使用场景提供最合理的默认设置。
|
|||
|
|
|
|||
|
|
要使查询循环变体正常工作,我们需要:
|
|||
|
|
- 为`core/query`区块注册具有某些默认值的区块变体
|
|||
|
|
- 为区块变体定义布局
|
|||
|
|
- 在`isActive`区块变体属性中使用`namespace`属性
|
|||
|
|
|
|||
|
|
让我们以为一个注册了`book`自定义文章类型的插件设置变体为例,开始这段旅程。
|
|||
|
|
|
|||
|
|
### 1. 提供合理的默认值
|
|||
|
|
|
|||
|
|
您的第一步是创建一个变体,该变体将默认显示书籍列表而非博客文章。完整的变体代码如下所示:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
const MY_VARIATION_NAME = 'my-plugin/books-list';
|
|||
|
|
|
|||
|
|
registerBlockVariation( 'core/query', {
|
|||
|
|
name: MY_VARIATION_NAME,
|
|||
|
|
title: '书籍列表',
|
|||
|
|
description: '显示书籍列表',
|
|||
|
|
isActive: ( { namespace, query } ) => {
|
|||
|
|
return (
|
|||
|
|
namespace === MY_VARIATION_NAME
|
|||
|
|
&& query.postType === 'book'
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
icon: /** 此处可放置SVG图标 */,
|
|||
|
|
attributes: {
|
|||
|
|
namespace: MY_VARIATION_NAME,
|
|||
|
|
query: {
|
|||
|
|
perPage: 6,
|
|||
|
|
pages: 0,
|
|||
|
|
offset: 0,
|
|||
|
|
postType: 'book',
|
|||
|
|
order: 'desc',
|
|||
|
|
orderBy: 'date',
|
|||
|
|
author: '',
|
|||
|
|
search: '',
|
|||
|
|
exclude: [],
|
|||
|
|
sticky: '',
|
|||
|
|
inherit: false,
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
scope: [ 'inserter' ],
|
|||
|
|
}
|
|||
|
|
);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如果这看起来很多,请不要担心,让我们逐一查看这里的属性,了解它们的存在意义和作用。
|
|||
|
|
|
|||
|
|
本质上,您可以从这样的代码开始:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
registerBlockVariation( 'core/query', {
|
|||
|
|
name: 'my-plugin/books-list',
|
|||
|
|
attributes: {
|
|||
|
|
query: {
|
|||
|
|
/** ...更多查询设置(如需要) */
|
|||
|
|
postType: 'book',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
} );
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
通过这种方式,用户无需从下拉菜单中选择自定义的`postType`,即可直接获得正确的配置。但您可能会问,用户如何找到并插入这个变体?好问题!要实现这一点,您应该添加:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
{
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
scope: [ 'inserter' ],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这样,当用户在编辑器中搜索时,您的区块将像任何其他区块一样显示。此时,您可能还想为变体添加自定义图标、标题和描述,如下所示:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
{
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
title: '书籍列表',
|
|||
|
|
description: '显示书籍列表',
|
|||
|
|
icon: /* 您的SVG图标在此 */,
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
至此,您的自定义变体将与独立区块几乎无法区分。完全为您的插件品牌化,易于发现,并作为即插即用的选项直接提供给用户。
|
|||
|
|
|
|||
|
|
然而,您的查询循环变体尚不能正常工作——我们仍需定义布局以便其正确渲染。
|
|||
|
|
|
|||
|
|
## 扩展查询功能
|
|||
|
|
|
|||
|
|
即便具备上述所有功能,您的自定义文章类型可能仍有独特需求:可能需要支持某些用于筛选和查询的自定义属性,或者某些查询参数可能无关紧要甚至完全不支持!我们在设计查询循环区块时已考虑到这类使用场景,下面来看看如何解决这个问题。
|
|||
|
|
|
|||
|
|
### 禁用无关或不支持的查询控件
|
|||
|
|
|
|||
|
|
假设您的图书数据完全未使用`sticky`属性,那么该设置对区块定制将毫无意义。为避免用户困惑并呈现清晰的交互界面,我们需要隐藏此控件。再假设您完全未使用`author`字段(该字段通常表示将文章添加到数据库的用户),而是使用自定义的`bookAuthor`字段。此时若保留`author`筛选器不仅会造成混淆,更会直接"破坏"您的查询。
|
|||
|
|
|
|||
|
|
为此,查询循环区块变体支持名为`allowedControls`的属性,该属性接收数组参数用于指定要在检查器侧边栏显示的控件键值。默认情况下会显示所有控件,但一旦设置该属性,我们就需要明确指定相关控件!
|
|||
|
|
|
|||
|
|
截至Gutenberg 14.2版本,可用控件如下:
|
|||
|
|
|
|||
|
|
- `inherit` - 显示切换开关,允许查询直接从模板继承
|
|||
|
|
- `postType` - 显示可用文章类型下拉菜单
|
|||
|
|
- `order` - 显示查询排序方式下拉菜单
|
|||
|
|
- `sticky` - 显示置顶文章处理方式下拉菜单
|
|||
|
|
- `taxQuery` - 显示当前所选文章类型的分类法筛选器
|
|||
|
|
- `author` - 显示按作者筛选查询的输入字段
|
|||
|
|
- `search` - 显示按关键词筛选查询的输入字段
|
|||
|
|
- `format` - 显示按[格式集合](https://developer.wordpress.org/advanced-administration/wordpress/post-formats/#supported-formats)筛选查询的输入字段
|
|||
|
|
- `parents` - 显示使用父实体筛选查询的输入字段
|
|||
|
|
|
|||
|
|
在本案例中,该属性配置如下:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
{
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
allowedControls: [ 'inherit', 'order', 'taxQuery', 'search' ],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
如需隐藏所有可用控件,可将`allowedControls`值设为空数组。
|
|||
|
|
|
|||
|
|
注意我们同时禁用了`postType`控件。当用户选择我们的变体时,为何还要显示令人困惑的文章类型下拉菜单?更重要的是,正如稍后将介绍的,这可能会破坏我们实现自定义控件的区块功能。
|
|||
|
|
|
|||
|
|
### 添加额外控件
|
|||
|
|
|
|||
|
|
由于我们的插件需要使用自定义属性进行查询,我们需要添加专属控件来替代刚刚从核心检查器禁用的控件。这可以通过接入[区块过滤器](https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/)的[React HOC](https://reactjs.org/docs/higher-order-components.html)实现:
|
|||
|
|
|
|||
|
|
```jsx
|
|||
|
|
import { InspectorControls } from '@wordpress/block-editor';
|
|||
|
|
|
|||
|
|
export const withBookQueryControls = ( BlockEdit ) => ( props ) => {
|
|||
|
|
// 仅当属于我们的变体时才添加这些控件
|
|||
|
|
// 此处可实现自定义逻辑进行验证,类似于前述的`isActive`函数
|
|||
|
|
// 以下假设您已编写自定义的`isMyBooksVariation`函数进行处理
|
|||
|
|
return isMyBooksVariation( props ) ? (
|
|||
|
|
<>
|
|||
|
|
<BlockEdit key="edit" { ...props } />
|
|||
|
|
<InspectorControls>
|
|||
|
|
<BookAuthorSelector /> { /** 我们的自定义组件 */ }
|
|||
|
|
</InspectorControls>
|
|||
|
|
</>
|
|||
|
|
) : (
|
|||
|
|
<BlockEdit key="edit" { ...props } />
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
addFilter( 'editor.BlockEdit', 'core/query', withBookQueryControls );
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
当然,您需要自行实现控件的逻辑(建议参考[`@wordpress/components`](https://www.npmjs.com/package/@wordpress/components)使控件完美融入Gutenberg界面)。通过少量额外工作,您在区块属性的`query`对象内设置的任何额外参数,都可用于创建符合需求的自定义查询。
|
|||
|
|
|
|||
|
|
当前您可能需要实现略有差异的路径,以确保在前端(即最终用户侧)正确执行查询,并在编辑器端显示正确的预览。
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
{
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
attributes: {
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
query: {
|
|||
|
|
/** ...更多查询设置(如需要) */
|
|||
|
|
postType: 'book',
|
|||
|
|
/** 我们的自定义查询参数 */
|
|||
|
|
bookAuthor: 'J. R. R. Tolkien'
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 自定义变体布局
|
|||
|
|
|
|||
|
|
请注意,查询循环区块支持在 `scope` 属性中使用字符串 `'block'`。理论上,这是为了让变体在插入区块后被识别。更多关于区块变体选择器的信息请[参阅此处](https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-variation-picker/README.md)。
|
|||
|
|
|
|||
|
|
然而,目前**不建议**使用此方式,这是因为查询循环通过模式和 `scope: [ 'block' ]` 变体进行设置时,所选模式的所有属性将被使用,但 `postType` 和 `inherit` 查询属性除外,这可能导致冲突和变体功能异常。
|
|||
|
|
|
|||
|
|
为解决此问题,有两种方法。第一种是添加默认的 `innerBlocks`,如下所示:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
innerBlocks: [
|
|||
|
|
[
|
|||
|
|
'core/post-template',
|
|||
|
|
{},
|
|||
|
|
[ [ 'core/post-title' ], [ 'core/post-excerpt' ] ],
|
|||
|
|
],
|
|||
|
|
[ 'core/query-pagination' ],
|
|||
|
|
[ 'core/query-no-results' ],
|
|||
|
|
],
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
通过在变体中包含 `innerBlocks`,您实际上跳过了查询循环区块通过建议模式进行设置的阶段,区块将以这些内部区块作为初始内容插入。
|
|||
|
|
|
|||
|
|
另一种方法是为您的变体注册特定模式,这些模式将在设置过程中被推荐,并替换区块的流程。
|
|||
|
|
|
|||
|
|
查询循环区块会判断自身是否有活跃变体,以及该变体是否有特定模式可用。如果有,这些模式将是唯一推荐给用户的模式,不会包含原始查询循环区块的默认模式。否则,如果没有此类模式,将推荐默认模式。
|
|||
|
|
|
|||
|
|
若要将模式与查询循环变体“关联”,您应在模式的 `blockTypes` 属性中添加以查询循环名称前缀的变体名称(例如 `core/query/$variation_name`)。有关注册模式的更多详情,请[参阅此处](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-patterns/)。
|
|||
|
|
|
|||
|
|
如果您未在变体中提供 `innerBlocks`,还有一种方法可以在用户于设置阶段选择 `Start blank` 时推荐“关联”变体。这与“关联”模式的处理方式类似,通过检查查询循环是否有活跃变体以及是否有任何关联变体可推荐。
|
|||
|
|
|
|||
|
|
若要将一个变体与另一个查询循环变体关联,我们需要将 `scope` 属性定义为 `['block']`,并将 `namespace` 属性定义为一个数组。该数组应包含希望关联的变体的名称(`name` 属性)。
|
|||
|
|
|
|||
|
|
例如,如果我们有一个名称为 `products` 的查询循环变体暴露于插入器中(`scope: ['inserter']`),我们可以通过将其 `namespace` 属性设置为 `['products']` 来关联一个作用域为 `block` 的变体。如果用户在点击 `Start blank` 后选择此变体,`namespace` 属性将被主要的插入器变体覆盖。
|
|||
|
|
|
|||
|
|
### 3. 让 Gutenberg 识别您的变体
|
|||
|
|
|
|||
|
|
在实现此变体后,您可能意识到一个小问题:虽然用户在插入时对此无感知,但 Gutenberg 仍会将变体识别为查询循环区块的核心功能,因此在插入后,例如在编辑器的树形视图中,它将显示为查询循环区块。
|
|||
|
|
|
|||
|
|
我们需要一种方式告诉编辑器,此区块实际上是您的特定变体。这正是 `isActive` 属性的用途:它是一种基于区块属性判断特定变体是否活跃的方法。您可以像这样使用它:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
{
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
isActive: ( { namespace, query } ) => {
|
|||
|
|
return (
|
|||
|
|
namespace === MY_VARIATION_NAME
|
|||
|
|
&& query.postType === 'book'
|
|||
|
|
);
|
|||
|
|
},
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
您可能倾向于仅比较 `postType`,以便当 `postType` 匹配 `book` 时,Gutenberg 会将区块识别为您的变体。然而,这种方式范围过广,因为其他插件可能也希望基于 `book` 文章类型发布变体,或者我们可能不希望用户在编辑器设置中手动将类型设置为 `book` 时每次都识别为变体。
|
|||
|
|
|
|||
|
|
这就是为什么查询循环区块暴露了一个名为 `namespace` 的特殊属性。它在区块实现中实际上没有任何作用,而是作为一种简单且一致的方式供扩展者识别和限定自己的变体。此外,`isActive` 还可以接受一个字符串数组,用于比较属性。通常,`namespace` 就足够了,您可以这样使用:
|
|||
|
|
|
|||
|
|
```js
|
|||
|
|
{
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
attributes: {
|
|||
|
|
/** ...变体属性 */
|
|||
|
|
namespace: 'my-plugin/books-list',
|
|||
|
|
},
|
|||
|
|
isActive: [ 'namespace' ],
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
这样,Gutenberg 只有在匹配您的自定义命名空间时才会知道这是您的特定变体!非常方便!
|