使用 Block Bindings 构建书评网站,第二部分:查询、模式和模板
距离我们上次一起使用自定义字段构建书评网站已经过去一个多月了。在本系列的第一部分中,我们设想了一个客户项目,其限制条件使得您没有时间或资源来创建自定义块。替代方案是使用自定义字段和Block Bindings API。
在上篇文章中,您学习了如何:
- 注册自定义字段并将其绑定到块属性。
- 添加自定义块变体以绕过手动编码已连接的块。
- 构建自定义元数据输入字段。
- 以及其他一些技巧。
其中很多内容感觉像是“设置”阶段,只是让一切运行起来。本教程系列的第二部分将在此基础上构建,并向您展示在块主题中使用自定义字段的实际步骤。
请记住,您在学习本教程时可以随时参考GitHub 仓库或Playground 演示。
按元数据查询文章
让我们从一些有趣的事情开始:构建一个自定义的 Query Loop 块变体,用于按评分列出书评。其中很大一部分是在检查器面板中添加一个自定义的评分选择器,如截图所示:
在为本教程构建初始代码时,我做的第一件事就是重新阅读了 2022 年我在开发者博客上发布的一篇教程:使用 Query Loop 块变体构建书评网格。
那时,我们还没有 Block Bindings API 可用,但基础部分已经存在。对于本教程的这一部分,我基本上只是复制粘贴了之前的工作——并进行了一些小的修改。我强烈建议您阅读那篇文章,以更深入地了解这项技术。
添加评分下拉控件
在按元数据查询文章之前,您需要一种方法来选择要查询的元数据。在本系列的第一部分,您注册了几个元键。这里提醒一下它们的作用:
themeslug_book_author: 书籍作者姓名。themeslug_book_rating: 用户对书籍的星级评分。themeslug_book_length: 书籍的页数。themeslug_book_goodreads_url: 书籍在 Goodreads.com 上的页面 URL。
当然,您可以按其中任何字段查询文章。但本次练习,我们专注于 themeslug_book_rating 元键。您可以自行探索按其他键查询。
首先,将此代码添加到您的 /resources/js 目录下的 editor.js 文件中,以导入空的 query.js 脚本:
import './query';
然后打开您的 resources/js/query.js 文件,通过添加以下代码导入一些必要的依赖项:
import { __ } from '@wordpress/i18n';
import { addFilter } from '@wordpress/hooks';
import { InspectorControls } from '@wordpress/block-editor';
import { PanelBody, SelectControl } from '@wordpress/components';
现在,让我们构建如上图所示的书评面板和评分控件。为此,您将依赖几个内置的 WordPress 组件:
<PanelBody>: 一个自定义面板,用于容纳您想要显示的任何控件。<SelectControl>: 一个下拉选择字段,用于选择书籍评分。
将此代码添加到您的 query.js 文件中:
const BookReviewControls = ( { props: {
attributes,
setAttributes
} } ) => {
const { query } = attributes;
return (
<PanelBody title={ __( 'Book Review', 'themeslug' ) }>
<SelectControl
label={ __( 'Rating', 'themeslug' ) }
value={ query?.bookRating || '' }
options={ [
{ value: '', label: '' },
{ value: 1, label: __( '1 Star', 'themeslug' ) },
{ value: 2, label: __( '2 Stars', 'themeslug' ) },
{ value: 3, label: __( '3 Stars', 'themeslug' ) },
{ value: 4, label: __( '4 Stars', 'themeslug' ) },
{ value: 5, label: __( '5 Stars', 'themeslug' ) }
] }
onChange={ ( value ) => {
setAttributes( {
query: {
...query,
bookRating: value
}
} );
} }
/>
</PanelBody>
);
};
请特别注意 onChange 属性和对 setAttributes() 的调用。尤其要注意,代码传递了一个名为 bookRating 的自定义属性及其评分值。在后续步骤中过滤查询时,您将使用此属性。
要使自定义的书评面板和评分控件出现在 Query Loop 块中,您需要过滤 editor.BlockEdit 并将您的控件附加到该块。
现在将此代码添加到您的 query.js 文件中:
const withBookReviewControls = ( BlockEdit ) => ( props ) => {
return 'core/query' === props.name ? (
<>
<BlockEdit {...props} />
<InspectorControls>
<BookReviewControls props={props} />
</InspectorControls>
<>
) : (
<BlockEdit {...props} />
);
};
addFilter( 'editor.BlockEdit', 'core/query', withBookReviewControls );
由于您正在构建的网站只有书评文章,因此不需要其他检查。此面板和控件已添加到所有 Query Loop 块,但您也可以将过滤器包装在附加条件中,以进一步限制面板显示的位置。
过滤查询
添加评分控件实际上并不会改变查询到的文章。您还必须为在编辑器和前端查询文章时添加过滤器。这需要少量的 PHP 代码。
要使基于元数据的查询在编辑器中工作,您必须过滤 rest_post_query 钩子。每当通过 REST API 查询文章时,都会运行此过滤器钩子。
打开您主题的 functions.php 并添加此代码:
add_filter( 'rest_post_query', 'themeslug_rest_book_reviews', 10, 2 );
function themeslug_rest_book_reviews( $args, $request ) {
$rating = $request->get_param( 'bookRating' );
if ( $rating ) {
$args['meta_key'] = 'themeslug_book_rating';
$args['meta_value'] = absint( $rating );
}
return $args;
}
请注意,代码检查了您在 JavaScript 中设置的 bookRating 属性。如果已设置,它会将 themeslug_book_rating 元键和值作为查询参数传递。
您需要采取类似的步骤才能使其在前端工作,但这更像是一个两步过程:
- 您必须首先过滤
pre_render_block以检查块属性中是否已设置bookRating属性。 - 如果设置了该属性,则过滤
query_loop_block_query_vars以添加元键和值查询参数。
将此代码添加到您主题的 functions.php 文件中:
add_filter( 'pre_render_block', 'themeslug_pre_render_block', 10, 2 );
function themeslug_pre_render_block( $pre_render, $parsed_block ) {
if (
isset( $parsed_block['attrs']['query']['bookRating'] )
&& absint( $parsed_block['attrs']['query']['bookRating'] ) > 0
) {
add_filter(
'query_loop_block_query_vars',
function( $query ) use ( $parsed_block ) {
$query['meta_key'] = 'themeslug_book_rating';
$query['meta_value'] = absint( $parsed_block['attrs']['query']['bookRating'] );
return $query;
}
);
}
return $pre_render;
}
从此刻起,您可以按任意喜欢的评分查询书评。您可以在页面、模板甚至模式中使用它。
模式
对我来说,使用块的一半乐趣在于模式系统。感觉我们做了很多设置才到了我最喜欢的部分,但当所有部分都就位,可以真正开始设计时,这一切都是值得的。
在注册模式之前,通过将此代码添加到您的 functions.php 文件中,注册一个书评模式类别来容纳您的自定义模式:
add_action( 'init', 'themeslug_register_pattern_categories' );
function themeslug_register_pattern_categories() {
register_block_pattern_category( 'themeslug-book-review', [
'label' => __( 'Book Reviews', 'themeslug' )
] );
}
书评卡片
假设您的客户希望有一种快速的方法将“书评卡片”插入到他们的文章中。您可以用很多方法来实现这一点,所以请随意尝试。但我想构建一个包含以下内容的卡片:
- 特色图片。
- 每个已注册的自定义字段和块绑定。
- 书籍的自定义引文。
这是最终结果:
由于这是一个模式,您的客户可以通过几次鼠标点击插入它,并为特定的书评输入自定义数据。
如果您还记得本系列的第一部分,您为每个连接到自定义字段的块创建了块变体。这意味着您可以直接从编辑器构建此模式,而无需在可视编辑器和代码编辑器之间切换。
所以,此时您甚至不是在编码——只是直接在编辑器中构建有趣的东西。
我鼓励您完全自己构建这个书评卡片,并将其注册为模式。但为了快速复制/粘贴解决方案,请在您主题的 /patterns 文件夹中创建一个名为 book-review-card.php 的新文件,并向其中添加以下代码:
<?php
/**
* Title: Book Review Card
* Slug: themeslug/book-review-card
* Categories: themeslug-book-review
* Viewport Width: 1376
*/
?>
<!-- wp:columns {"verticalAlignment":"center","align":"wide","style":{"spacing":{"padding":{"top":"var:preset|spacing|30","bottom":"var:preset|spacing|30","left":"var:preset|spacing|30","right":"var:preset|spacing|30"},"blockGap":{"top":"var:preset|spacing|40","left":"var:preset|spacing|30"}}},"backgroundColor":"accent"} -->
<div class="wp-block-columns alignwide are-vertically-aligned-center has-accent-background-color has-background" style="padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--30);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--30)">
<!-- wp:column {"verticalAlignment":"center","width":"33.33%"} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:33.33%">
<!-- wp:post-featured-image {"aspectRatio":"3/4","style":{"border":{"radius":"0px"}}} /-->
</div>
<!-- /wp:column -->
<!-- wp:column {"verticalAlignment":"center","width":"66.66%","style":{"spacing":{"blockGap":"var:preset|spacing|40"}}} -->
<div class="wp-block-column is-vertically-aligned-center" style="flex-basis:66.66%">
<!-- wp:group {"style":{"spacing":{"blockGap":"var:preset|spacing|10"}},"layout":{"type":"flex","orientation":"vertical"}} -->
<div class="wp-block-group">
<!-- wp:group {"style":{"spacing":{"blockGap":"0.25em"}},"layout":{"type":"flex","flexWrap":"nowrap"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p>⭐️</p>
<!-- /wp:paragraph -->
<!-- wp:paragraph {"placeholder":"<?php esc_attr_e( 'Book Rating', 'themeslug' ); ?>","metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"themeslug_book_rating"}}}}} -->
<p></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><?php esc_html_e( '/ 5 Stars', 'themeslug' ); ?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
<!-- wp:group {"style":{"spacing":{"blockGap":"0.25em"}},"layout":{"type":"flex","flexWrap":"nowrap"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p><strong>📃</strong></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph {"placeholder":"<?php esc_attr_e( 'Book Length', 'themeslug' ); ?>","metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"themeslug_book_length"}}}}} -->
<p></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph -->
<p><?php esc_html_e( 'Pages', 'themeslug' ); ?></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
<!-- wp:group {"style":{"spacing":{"blockGap":"0.25em"}},"layout":{"type":"flex","flexWrap":"nowrap"}} -->
<div class="wp-block-group">
<!-- wp:paragraph -->
<p><?php esc_html_e( '✍️ Written by', 'themeslug' ); ?></p>
<!-- /wp:paragraph -->
<!-- wp:paragraph {"placeholder":"<?php esc_attr_e( 'Book Author', 'themeslug' ); ?>","metadata":{"bindings":{"content":{"source":"core/post-meta","args":{"key":"themeslug_book_author"}}}}} -->
<p></p>
<!-- /wp:paragraph -->
</div>
<!-- /wp:group -->
<!-- wp:buttons -->
<div class="wp-block-buttons">
<!-- wp:button {"metadata":{"bindings":{"url":{"source":"core/post-meta","args":{"key":"themeslug_book_goodreads_url"}}}}} -->
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button"><?php esc_html_e( 'View on Goodreads <span aria-hidden="true" class="wp-exclude-emoji">→</span>', 'themeslug' ); ?></a></div>
<!-- /wp:button -->
</div>
<!-- /wp:buttons -->
</div>
<!-- /wp:group -->
<!-- wp:pullquote {"textAlign":"left","style":{"typography":{"fontSize":"1.2rem"},"spacing":{"padding":{"top":"0","bottom":"0"}}},"className":"is-style-plain"} -->
<figure class="wp-block-pullquote has-text-align-left is-style-plain" style="padding-top:0;padding-bottom:0;font-size:1.2rem"><blockquote><p></p></blockquote></figure>
<!-- /wp:pullquote -->
</div>
<!-- /wp:column -->
</div>
<!-- /wp:columns -->
请注意,我通过格式化块标记和国际化文本字符串清理了模式文件,正如模式文档所推荐的。
书评查询网格
与之前的模式类似,您现在可以完全从编辑器构建自定义的 Query Loop 块模式,同时包含自定义字段。
看看这个将内部 Post Template 块设置为三列网格布局的模式:
嵌套在 Post Template 内部的是这些块:
- 文章特色图片。
- 包含两个块的行:
- 包含“By”前缀文本的段落。
- 书籍作者变体。
- 包含三个块的行:
- 带有 ⭐ 表情符号的段落。
- 书籍评分变体。
- 带有“Stars”后缀文本的段落。
嗯,这就是它的本质。我鼓励您尝试在