跳到主要内容

HPOS 扩展使用指南

什么是高性能订单存储 (HPOS)?

直到最近,WooCommerce 将与订单相关的数据存储在数据库中的 postpostmeta 表中,作为一种自定义 WordPress 帖子类型。这使得生态系统中的每个人都可以利用 WordPress 核心提供的广泛 API 来管理订单,并将订单视为自定义文章类型。

然而,在 2022 年初,我们 宣布了迁移到专用于订单的表的计划。将订单存储在自己的表中,可以使商店更容易扩展、简化数据存储并提高可靠性。有关更多详细信息,请查看我们 关于数据库结构的深入分析

通常,WooCommerce 努力与旧版本完全兼容,但是,由于这个项目,扩展开发者需要对他们的插件进行一些更改,以利用 HPOS。这是因为底层的数据结构发生了根本性的变化。

更具体地说,开发者不再需要使用 WordPress 提供的 API 来访问订单数据,而是需要使用 WooCommerce 专用的 API。我们 在 WooCommerce 版本 3.0 中引入了这些 API,以简化向 HPOS 的过渡。

在本指南中,我们将重点介绍使扩展程序或任何自定义代码片段与 HPOS 兼容所需的更改。

有关如何启用或禁用 HPOS 的详细信息,以及有关订单如何在不同数据存储之间同步的详细信息,请参阅 HPOS 文档

向后兼容性

为了使商店和开发者都能更轻松地过渡,我们尽可能地保持向后兼容。这个项目的主要兼容性问题之一是,由于底层的数据结构是 wp_postswp_postmeta 表,因此绕过 WooCommerce 专用的 CRUD 类,直接使用 WordPress API 访问数据是可行的。

现在情况已经改变,直接从这些 WordPress 表中读取数据可能会读取到过时的订单,而直接写入这些表可能会更新一个无法读取的订单。由于这是一个非常严重的问题,我们为过渡期添加了一些缓解措施。

切换数据源

此外,如果您在使用新表时遇到问题,您可以手动从 HPOS 切换回 post 表。要切换回旧模式,请在 WC > 设置 > 高级 > 功能中更改“订单数据存储”设置。

支持扩展中的高性能订单存储

虽然向后兼容性政策使商家更容易使用该项目,但对于扩展开发者来说,这意味着您需要同时支持 Posts 和 HPOS 运行一段时间。

为了帮助开发者,我们提供了一些指南供您参考。

注意: 我们建议您在开发扩展时使用 WooCommerce 的开发版本,以便获得最新的 HPOS 修复和 API。请参考我们的 开发指南,了解 WooCommerce 仓库的结构以及如何从源代码构建插件。

检测商店是否正在使用 HPOS 表格

虽然 WooCommerce CRUD API 通常可以使您在大多数情况下同时支持 Posts 和自定义表格,但在某些情况下(例如,当您编写 SQL 查询以提高性能时),您可能需要知道商店是否正在使用 HPOS 表格。在这种情况下,您可以使用以下模式:

use Automattic\WooCommerce\Utilities\OrderUtil;

if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
// HPOS 已启用。
} else {
// 正在使用传统的基于 CPT 的订单。
}

检查代码库中是否存在直接数据库访问

为了支持 HPOS 表格,一个好的起点是检查您的代码库中是否存在直接数据库访问以及使用不再应用于处理订单的 WordPress API。您可以使用以下正则表达式进行此检查:

wpdb|get_post|get_post_field|get_post_status|get_post_type|get_post_type_object|get_posts|metadata_exists|get_post_meta|get_metadata|get_metadata_raw|get_metadata_default|get_metadata_by_mid|wp_insert_post|add_metadata|add_post_meta|wp_update_post|update_post_meta|update_metadata|update_metadata_by_mid|delete_metadata|delete_post_meta|delete_metadata_by_mid|delete_post_meta_by_key|wp_delete_post|wp_trash_post|wp_untrash_post|wp_transition_post_status|clean_post_cache|update_post_caches|update_postmeta_cache|post_exists|wp_count_post|shop_order

请注意,您会发现很多误报,但此正则表达式相当全面,应该可以捕获大多数真正的匹配项。

在您的源代码中搜索上述正则表达式,并:

  1. 逐个检查匹配项,并检查该匹配项是否与订单相关。 大多数匹配项很可能都是误报,即它们与订单无关。
  2. 如果您看到某个匹配项直接访问或修改订单数据,则需要将其更改为使用 WooCommerce 的 CRUD API。

获取/设置文章和文章元数据的 API

任何直接获取文章的代码都可以转换为 wc_get_order 调用:

// 替代方案
$post = get_post( $post_id ); // 返回 WP_Post 对象。
// 使用
$order = wc_get_order( $post_id ); // 返回 WC_Order 对象。

要与元数据进行交互,请使用订单对象的 update_/add_/delete_metadata 方法,然后调用 save。 WooCommerce 会自动处理确定哪些表处于活动状态,并将数据保存在适当的位置。

// 替代以下更新/添加/删除方法,使用:
update_post_meta( $post_id, $meta_key_1, $meta_value_1 );
add_post_meta( $post_id, $meta_key_2, $meta_value_2 );
delete_post_meta( $post_id, $meta_key_3, $meta_value_3 );

// 使用
$order = wc_get_order( $post_id );
$order->update_meta_data( $meta_key_1, $meta_value_1 );
$order->add_meta_data( $meta_key_2, $meta_value_2 );
$order->delete_meta_data( $meta_key_3, $meta_value_3 );
$order->save();

注意: 调用 save() 方法是一个相对耗时的操作,因此您可能希望避免不必要地多次调用它(例如,如果您知道稍后将在同一流程中调用它,则在对同一对象进行操作时,您可能希望避免额外的早期调用)。

要获取订单的确切类型,或者检查给定的 ID 是否为订单,可以使用 OrderUtil 类的 方法

// 检查 ID 是否为订单的模式
'shop_order' === get_post_type( $post_id ); // 或者
in_array( get_post_type( $post_type ), wc_get_order_types() );

// 替换为:
use Automattic\WooCommerce\Utilities\OrderUtil;
'shop_order' === OrderUtil::get_order_type( $post_id ); // 或者
OrderUtil::is_order( $post_id, wc_get_order_types() );

订单管理界面函数审计

由于 WC 无法使用 WordPress 提供的帖子列表和帖子编辑界面,我们还添加了新的订单管理界面。这些界面与您在 WooCommerce 管理后台中看到的界面非常相似(除了它们使用 HPOS 表)。您可以使用以下正则表达式来执行此审计:

post_updated_messages|do_meta_boxes|enter_title_here|edit_form_before_permalink|edit_form_after_title|edit_form_after_editor|submitpage_box|submitpost_box|edit_form_advanced|dbx_post_sidebar|manage_shop_order_posts_columns|manage_shop_order_posts_custom_column

您可能会在这里看到很多误报。但是,如果您发现这些方法被用于订单界面,则需要进行以下更改才能将其升级到 HPOS:

您需要使用 WC_Order 类的 $order 对象,而不是 WP_Post 类的 $post 对象。如果它是一个过滤器或操作,我们将在新的 WooCommerce 界面中实现类似的过滤器,并且它将接受一个 WC_Order 对象,而不是传递帖子对象。

以下代码片段展示了一种在旧的订单编辑器界面(当使用旧订单时)和新的 HPOS 驱动的编辑器界面中添加元数据框的方法:

use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;

add_action( 'add_meta_boxes', 'add_xyz_metabox' );

function add_xyz_metabox() {
$screen = class_exists( '\Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController' ) && wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled()
? wc_get_page_screen_id( 'shop-order' )
: 'shop_order';

add_meta_box(
'xyz',
'自定义元数据框',
'render_xyz_metabox',
$screen,
'side',
'high'
);
}

上述代码还会更改传递给元数据框的参数,因此在您的元数据框中,您需要考虑到可能传递的帖子对象或订单对象。我们建议获取订单对象并完全使用它,而不是使用传递的参数。

function render_xyz_metabox( $post_or_order_object ) {
$order = ( $post_or_order_object instanceof WP_Post ) ? wc_get_order( $post_or_order_object->ID ) : $post_or_order_object;

// ... 代码的其余部分。在此点之后,不应直接使用 $post_or_order_object。
}

声明扩展的兼容性 (或不兼容性)

在您检查了扩展的代码后,您可以声明该扩展是否与 HPOS 兼容。 我们提供了一个 API 来简化此过程。 要声明您的扩展兼容,请将以下代码放置在您的主插件文件中:

add_action( 'before_woocommerce_init', function() {
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, true );
}
} );

如果您知道您的代码不支持 HPOS,您应该以以下方式声明不兼容性。 将以下代码放置在您的主插件文件中:

add_action( 'before_woocommerce_init', function() {
if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 'custom_order_tables', __FILE__, false );
}
} );

如果您希望在主插件文件之外包含兼容性声明,请将 'my-plugin-slug/my-plugin.php' 传递给上面的代码片段中的 __FILE__ 参数,而不是直接使用 __FILE__

为了防止出现问题,如果任何不兼容的插件处于激活状态,WooCommerce 会向用户发出警告,提示他们不要启用 HPOS。 它还会在插件屏幕显示一个警告,以确保用户了解该扩展是否不兼容。

由于许多 WordPress 扩展与 WooCommerce 无关,因此 WC 只会为在主插件文件的标题中声明了 WC tested up to 的扩展显示信息

新的订单查询 API

HPOS 通过 WC_Order_Query 引入了新的查询类型,这些类型允许进行更复杂的订单查询,涉及日期、元数据和订单字段。 请访问 HPOS: 新的订单查询 API 以获取详情和示例。