创建自定义古腾堡块——第 10 部分:获取文章和高阶组件
在 Gutenberg 自定义块教程系列的最后一部分中,我们将学习如何使用高阶组件来利用 WordPress 的组件来执行对帖子和其他核心 WordPress 信息的查询。
在上一部分中,我们了解了动态块,我们最终实现了输入帖子 ID 并使用 PHP 动态获取帖子并在前端和预览模式下呈现它的功能。手动输入帖子 ID 不直观或用户友好。最好为用户提供某种方式来通过帖子标题选择或搜索帖子,然后单击某些内容来选择一个。
解决这个问题的一部分相当容易;如何从您的区块edit
功能中查询帖子。为此,我们有几个选择,最好的选择是使用 WordPress 中的一些所谓的高阶组件。您还可以使用 Javascript 浏览器方法使用例如fetch
或 axios 对 WordPress REST API 执行 AJAX 调用。WordPress 实际上提供了自己的fetch
:版本apiFetch()
。
解决这个问题的另一部分取决于你;这就是如何在我们的块中呈现列表或选项。您将如何呈现可供选择的帖子列表?在选择中,复选框或单选按钮列表?或者您想提供搜索的可能性,从而实现自动完成解决方案或过滤器解决方案?您应该允许选择多个帖子还是只选择一个?通常您可以通过使用不同的 WordPress 组件来解决这个问题,但是您需要决定要实施哪种解决方案。
让我们先了解一下高阶组件和 WordPress 数据模块,然后再了解如何在我们的块中执行后查询。
WordPress 核心数据模块和高阶组件
使用 React 时,您经常需要将状态向下传递给子组件或向上传递给公共父级组件,以便所有其他子组件都可以访问它们。解决集中应用程序状态问题的解决方案是使用Redux。使用 Redux,您可以构建存储——存储应用程序状态和信息的对象。
WordPress数据模块是不同商店的枢纽,提供管理不同模块之间数据的功能。它建立在 Redux 之上——但不要将它误认为是 WordPress 的 Redux,因为它们之间存在很多差异。您可以在 WordPress 中注册自己的商店,或者更重要的是,访问 WordPress 的注册商店。
这是 WordPress 数据模块中可用商店的概述(可能会随着时间的推移而改变)。所有 WordPress 的商店都包含在核心数据模块中。例如,有存储编辑器数据 ( core/editor
)、通知 ( core/notices
)、块数据 ( core/blocks
)、视口信息 ( core/viewport
),以及最后但并非最不重要的主存储本身 – core
。
为了从商店访问数据,您需要使用选择器。WordPress 在wp.data
包中有一个选择器;select()
. 您还可以使用 来操作商店dispatch
,但这不在本教程系列中。实际上,您可以真正轻松地亲自试用选择器,看看 WordPress 商店中有哪些可用的东西。
试用选择器
在 Chrome 中打开古腾堡编辑器,然后打开控制台调试器工具。输入:
wp.data.select(‘core’)
然后按 Enter。你应该得到一个对象作为你可以使用的所有选择器(函数)的响应。作为示例,您会发现getMedia
、getTaxonomy
、getAuthors
等函数。我们将用来查询帖子的那个也在那里,但没有直观的名称;它被称为getEntityRecords
。目前,其中一些功能已记录在案,但不幸的是,大多数都没有。
也可以尝试其他商店core
,例如:
wp.data.select(‘core/editor’).getBlocks()
这将返回有关您帖子中当前所有块的所有信息。你可以在 Chrome 控制台调试器中尝试这个,并尝试调用一些函数来查看你得到的响应。有些需要参数,有些则不需要。
为了使用选择器和访问存储,我们需要在高阶组件中使用它们。高阶组件只是 React 中做某事的一种模式。我们将组件传递给可以添加一些道具的函数(或组件),然后返回一个新组件。
在 WordPress 数据模块中,我们发现withSelect
;一个高阶组件,可用于使用已注册的选择器注入道具。换一种说法; 在withSelect
其中我们可以访问选择器select()
并可以使用它来执行调用。选择器结果将是我们传递给组件的道具withSelect
。如果您需要组合多个高阶组件,WordPress 数据模块提供了该compose
功能,但这不在本教程的讨论范围之内。我们将只使用一个高阶组件;withSelect
.
上面讲了很多理论,所以让我们开始看一些代码和实际例子。
使用 withSelect、select 和 getEntityRecords 获取帖子
综上所述,我们需要withSelect
为我们的块设置高阶组件。在这里面我们可以使用选择器来访问 WordPress 的商店,这将是我们传递给组件的道具withSelect
。我们将使用core
商店和选择器getEntityRecords
来查询帖子。
getEntityRecords
不幸的是,目前还没有很好地记录该功能。但我了解到我们可以将postType
第一个参数(实体类型)传递,然后将帖子类型作为第二个参数(例如“ post
”或“ page
”)传递。第三个参数是可选的,可以是带有查询参数的对象。稍后我们将查看第三个参数。
如果您按照上一部分中的本系列教程进行操作,您将拥有一个自定义块,该块接受在文本输入中手动输入的帖子 ID。该块使用 PHP 在前端(和预览模式)动态呈现帖子。让我们删除手动输入帖子 ID 的要求,并将其替换为更直观的内容。如前所述,您需要自己决定如何呈现帖子列表以及让用户选择帖子的最佳方式。为简单起见,我们将添加所有帖子标题的选择以供选择。
编码withSelect
让我们开始编码。首先我们需要从数据包中解构我们需要的东西;
const { withSelect, select } = wp.data;
然后我们withSelect
在块的edit
函数中使用并传递我们的编辑组件;FirstBlockEdit
. 在内部withSelect
,我们解构select
为参数并使用选择器select()
来查询带有getEntityRecords
. 我们返回一个对象,该对象具有我们调用的一个属性,该属性posts
保存调用的结果select()
。
…
edit: withSelect(select => {
return {
posts: select(‘core’).getEntityRecords(‘postType’, ‘post’)
}
})(FirstBlockEdit),
save: () => { return null }
…
使用我们组件上方的代码FirstBlockEdit
,现在将有一个新的道具;posts
. 无论我们在withSelect
高阶组件中返回什么,都可以作为我们传递的组件的 props 访问(在最后的括号中)。
处理来自选择器的文章
我们现在可以进入我们的组件FirstBlockEdit
并查看新的props.posts
. 因为我们的组件是基于类的组件,所以我们需要使用this
. render()
让我们在函数中的控制台中将其注销FirstBlockEdit
:
render() {
const { attributes, setAttributes } = this.props;
console.log(this.props.posts);
…
}
留意你的控制台调试器。您可能会注意到这将记录两次;首先null
,然后稍后它会记录一系列帖子。这是因为查询帖子是异步完成的。我们的组件首先在响应之前呈现,此时props.posts
是null
。一旦我们得到响应,我们的组件将再次重新渲染并填充 prop。您应该始终记住在代码中没有数据的情况下适应这个小时间段。
添加一个选择以显示帖子
让我们准备用返回的帖子填充选择,为此我们将使用 WordPress 组件SelectControl
。该组件SelectControl
接受一组选择,其中每个选择都是一个具有属性value
和的对象label
。
如果您查看控制台记录的(第二个)响应,您可以看到我们获得了一组 post 对象。每个帖子都包含帖子的大部分信息,但对于选择中的选择,我们只对作为值的帖子 ID 和作为标签的帖子标题感兴趣。所以我们将遍历posts
prop 并填充一个数组变量,我们将传递给SelectControl
. 不要忘记处理posts
道具所在的小时间范围null
。在这种情况下,我们将使用一个标签为“Loading…”的选项填充选择数组。
let choices = [];
if (this.props.posts) {
choices.push({ value: 0, label: __(‘Select a post’, ‘awhitepixel’) });
this.props.posts.forEach(post => {
choices.push({ value: post.id, label: post.title.rendered });
});
} else {
choices.push({ value: 0, label: __(‘Loading…’, ‘awhitepixel’) })
}
请注意,我们需要将帖子标题称为post.title.rendered
. 您可以在登录的控制台中查找自己,posts
并查看每个帖子的信息结构。
在此之后,我们只需要SelectControl
在我们想要的地方添加一个。它可以在块本身内(最好在编辑模式的代码内),或在 Inspector 内。
<SelectControl
label={__(‘Selected Post’, ‘awhitepixel’)}
options={choices}
value={attributes.selectedPostId}
onChange={(newval) => setAttributes({ selectedPostId: parseInt(newval) })}
/>
我们将 设置SelectControl
为引用selectedPostId
我们在上一步中定义的属性。我们在 prop 中设置保存的值并在 prop 中value
处理更新它onChange
——就像我们之前做过几次一样。我们确保使用 using 存储数字,parseInt()
因为selectedPostId
的类型为number
。我们在 prop 中传递生成的选择数组options
。
仅此而已!如果您遵循上一步中的代码,您应该已经有了读取保存的帖子 ID 并显示它的代码!
当然,我建议您以不同于简单选择的方式来实现帖子列表和选择。这不是一个漂亮或用户友好的解决方案,尤其是对于有很多帖子的网站。说到帖子数,您是否注意到选择器 getEntityRecords 最多只返回 10 个最新帖子?这是 getEntityRecords 的默认行为,但我们可以通过传递第三个参数来修改发布查询。
修改 getEntityRecords 的查询
通过将对象作为第三个参数传递给 getEntityRecords,我们可以修改发布查询。如前所述,不幸的getEntityRecords
是缺少文档。但是通过阅读整个网络,我收集了一个可能的查询参数列表;
per_page
:设置为一个数字来限制帖子的数量。设置为-1
以获取最大值 100。默认值10
。exclude
:从查询中排除某些帖子。设置为一个帖子 ID 或多个帖子 ID 的数字数组。parent_exclude
:排除某些父职位。设置为一个帖子 ID 或多个帖子 ID 的数组。orderby
: 决定帖子的顺序。您很可能可以使用与WP_Query 的 orderby中相同的参数。可以是例如 ‘menu_order
‘。order
:'asc'
或 ‘desc'
用于升序或降序。status
:按帖子状态过滤。可以是字符串、以逗号分隔的具有多个状态的字符串或状态字符串数组。例如['publish', 'draft']
,查询已发布和起草的帖子。categories
:按特定类别过滤帖子。提供类别 ID 或类别 ID 数组。我相信这只适用于帖子类别而不适用于其他自定义分类法。tags
:按某些标签过滤帖子。提供标签 ID 或标签 ID 数组。仅适用于帖子标签,不适用于其他自定义分类法。search
:添加搜索查询(字符串)。
让我们修改我们的查询。我们想做两件事;首先,我们要获取所有帖子,而不仅仅是最新的 10 篇。为此,我们提供-1
给per_page
。其次,我们想通过将当前帖子 ID 提供给 来从帖子列表中排除当前帖子exclude
。显示帖子快捷方式或当前帖子本身的预览通常没有意义。
你可能认为; 稍等,我们如何获得当前的帖子 ID?不要忘记我们在高阶组件中withSelect
并使用select
选择器可以访问所有 WordPress 的核心数据存储。当前的帖子 ID 很自然地存储在 WordPress 核心商店之一中。我们在其中core/editor
找到了功能getCurrentPostId()
。
让我们将withSelect
返回值修改成这样:
edit: withSelect(select => {
const currentPostId = select(‘core/editor’).getCurrentPostId();
const query = {
per_page: -1,
exclude: currentPostId
}
return {
posts: select(‘core’).getEntityRecords(‘postType’, ‘post’, query)
}
})(FirstBlockEdit),
上面的变化是不言自明的。我们生成一个具有属性的查询对象,per_page
并将exclude
其作为第三个参数传递给getEntityRecords()
. 现在我们props.posts
的组件内部FirstBlockEdit
应该列出所有帖子但排除当前帖子。
结论
这篇文章结束了如何创建自定义古腾堡块教程系列。该系列旨在介绍开发您自己的自定义块的基础知识,为您提供开发您自己的和更复杂的块的起点。一定要留意更多与古腾堡相关的教程。也许您会找到一个更具体地解释您想自己做的事情的教程!