+ 收藏我们

网站模板

网站模板搜索
404模板 营销型模板 外贸网站模板 单页模板 双语模板 标签大全
电话:18630701785
首页 > 站长学院 > 深入了解 WooCommerce HPOS 高性能订单存储 >

深入了解 WooCommerce HPOS 高性能订单存储

时间:2024-07-22 10:46:15

作为一个 WordPress 插件,WooCommerce 早起很自然的使用了自定义文章类型来存储商品和订单数据,当 WooCommerce 的逻辑越来越完善后,自定义文章类型的数据结构就带来了越来越严重的性能问题。 WooCommerce HPOS (High-Performance Order Storage) 就是 WooCommerce 团队为了解决WooCommerce 性能问题而开发的解决方案。

HPOS 究竟是什么?

HPOS(High-Performance Order Storage)是在数据库中存储 WooCommerce 订单的一种更高性能的存储的方式。自 8.2 版本起,新创建的商店默认就开启了 HPOS。以前,订单只是一个自定义文章类型shop_order,订单数据存储在wp_postswp_postmeta表中,但现在我们有一堆专门用于 WooCommerce 订单的数据库表。

数据库结构

接下来,我们看一下 HPOS 的数据结构,以及几个示例,通过这几个示例,大家可以了解到 HPOS 的存储方式为什么会比较高效。

实际上,我们需要关注的主有三个数据表。

首先是wp_wc_orders– 它存储了主要的订单信息。

wp_wc_orders WooCommerce HPOS database table

第二个是wp_wc_order_addresses– 这个数据表里面存储了账单和配送地址信息。

wp_wc_order_addresses WooCommerce HPOS database table

第三个数据表是wp_wc_order_operational_data– 这是对主要订单信息表的补充。,些字段与订单状态有关,并保存在一个单独的表中,因为预计该表的更改会更频繁。

wp_wc_order_operational_data WooCommerce HPOS database table

第四个数据是 wp_wc_orders_meta(等等,你说只有三个)。这个表并没有完全投入使用,但它与wp_postmeta有点类似,可以被其他插件使用。

wp_wc_orders_meta WooCommerce HPOS database table

有了上面的数据表,从数据库中获取并显示订单的过程会更加高效,下面让我们看一个简单的 SQL 查询,我们需要按账单国家获取订单。

在基于自定义文章类型存储订单数据的商店中,我们需要这样查询数据:

复制SELECT wp_posts.ID
FROM wp_posts
INNER JOIN wp_postmeta AS country ON wp_posts.ID = country.post_id AND meta_key = '_billing_country'
WHERE wp_posts.post_type = 'shop_order'
AND country.meta_value = 'DK'

在基于 HPOS 存储订单数据的商店中,获取同样数据的查询变成了下面的语句:

复制SELECT wp_wc_orders.id
FROM wp_wc_orders
INNER JOIN wp_wc_order_addresses AS address ON address.order_id = wp_wc_orders.id AND address.address_type = 'billing'
WHERE address.country = 'DK'

如果您的数据库中有 5000 个左右的订单,那么最后一个查询将快 4 倍!换句话说,您商店中的订单越多,HPOS 的性能就越高。其中一个原因是–我们不必再处理臃肿的wp_postswp_postmeta(尤其是wp_postmeta)。

如何开启 HPOS?

长话短说,您只需进入WooCommmerce > 设置 > 高级 > 功能,然后切换到基于 HPOS 的订单就可以了。

High performance order storage settings in WooCommerce

但有一些细微差别需要注意:

  • 自 WooCommerce 8.2 版起,所有新店默认启用 HPOS。
  • 如果您的插件不兼容,则无法切换到 HPOS。
  • 如果商店中有未同步的订单,则无法切换到 HPOS 或返回 HPOS(您可以顺便删除这些订单,或直接点击设置下方的 “同步 X 待处理订单 “链接)。
  • 还有一个 “兼容性模式 “复选框,其基本作用是同时使用高性能订单存储和基于 CPT 的订单存储(是的,它会复制订单,这对性能完全不利,但对兼容性非常有利)。

使您的插件与 HPOS 兼容

这是一个值得讨论的有趣话题。我可以肯定,很多人都在使用WP_Query来获取 WooCommerce 订单或商品数据,如果切换到了 HPOS 存储方式, 旧的获取数据的代码就会失效。但是,如果您从一开始就使用 CRUD 方法与 WooCommerce 订单进行交互,那么您可能根本不需要修改代码中的任何内容(我没有修改过我的插件中的任何一行 – 一切都和以前一样)就可以兼容 HPOS。

总之,让我们来看看我们必须牢记的几个要点。

代码更改示例

这里的关键是停止使用 WordPress 相关的文章查询函数,开始使用基于 WooCommerce CRUD 的函数、类及其方法。下面是一些示例。

获取订单信息:

复制// 停止使用这种方式:
// $post = get_post( $post_id ); // 返回 WP_Post 对象
// $order_status = $post->post_status;

// 使用这种方式:
$order = wc_get_order( $post_id ); // 返回 WC_Order 对象
$order_status = $order->get_status();

获取订单:

复制// 停止使用这种方式:
// $query = new WP_Query( array( 'post_type' => 'shop_order', 'posts_per_page' => 10 ) );

// 使用这种方式:
$query = new WC_Order_Query( array( 'limit' => 10 ) );
$orders = $query->get_orders();

// 停止使用这种方式:
// $orders = get_posts( array( 'post_type' => 'shop_order', 'posts_per_page' => 10 ) );

// 使用这种方式:
$orders = wc_get_orders( array( 'limit' => 10 ) );

获取和更新订单元数据:

复制// 停止使用这种方式:
// $custom_meta = get_post_meta( $order_id, '_misha_key', true );
// $custom_meta = $custom_meta .= 'test';
// update_post_meta( $order_id, '_misha_key', $custom_meta );

// 该用这种方式:
$order = wc_get_order( $order_id );
$custom_meta = $order->get_meta( '_misha_key', true );
$custom_meta = $custom_meta .= 'test';
$order->update_meta_data( '_misha_key', $custom_meta );
$order->save();

检查文章类型:

复制// 停止使用这种方式
// if( 'shop_order' === get_post_type( $post_id ) ) {

// 该用这种方式:
use Automattic\WooCommerce\Utilities\OrderUtil; // at the beginning of the file

if( 'shop_order' === OrderUtil::get_order_type( $post_id ) ) {

但由于 WooCommerce 会创建与 ID 匹配的符,因此也可以通过以下方式进行检查:'shop_order_placehold' === get_post_type( $post_id ),而无需使用OrderUtil类。

订单占位符

需要注意的一个有趣的向后兼容性问题是,每个 HPOS 订单都有自己的 “订单占位符”,即在wp_posts表中创建的自定义文章类型shop_order_placehold。它包含实际订单 ID,但数据库行中几乎所有其他列都是空的。wp_postmeta表中也没有写入任何内容。

example of a shop order placeholder in WooCommerce database

检查是否启用了 HPOS

官方文档建议使用OrderUtil类来执行检查。

复制use Automattic\WooCommerce\Utilities\OrderUtil;

if( OrderUtil::custom_orders_table_usage_is_enabled() ) {
	// HPOS 已启用.
} else {
	// 正在使用基于文章类型的订单
}

在插件中声明兼容性

好消息是,如果我们因为某种原因无法为自定义插件添加 HPOS 支持,我们可以让 WooCommerce 知道这一点。

下面是一个与高性能订单存储不兼容的插件示例:

复制<?php
/*
 * Plugin name: HPOS compatibility check
 * Version: 1.0
 * Author: Misha Rudrastyh
 * Author URI: https://rudrastyh.com
 */
add_action( 'before_woocommerce_init', 'wprs_hpos_compatibility' );

function wprs_hpos_compatibility() {
	
	if( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
		\Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility( 
			'custom_order_tables', 
			__FILE__, 
			false // true (compatible, default) or false (not compatible)
		);
	}
	
}

目前没有任何措施阻止您在 HPOS 商店中激活类似插件,但如果您已经有一个激活了该插件的商店,您就不能在设置中切换到 HPOS。

WooCommerce High Performance Order Storage plugins compatibility check您不能在激活了不兼容插件的情况下切换到 “高性能订单存储”。

屏幕 ID 更改

由于订单不再是文章类型(但仍使用相同的WP_List_Table类),因此我们不能在条件中使用标准的文章类型屏幕 ID。新的屏幕 ID 是woocommerce_page_wc-orders。好了,让我给你们举几个例子。

在第一个例子中,我们只是使用admin_notices操作钩子来打印一些内容。

复制add_action( 'admin_notices', 'wprs_woo_notice_example' );
function wprs_woo_notice_example() {
	
	$screen = get_current_screen();
	// 基于文章类型的订单中,我们这样判断:
	// if( 'edit-shop_order' === $screen->id ) {
	if( 'woocommerce_page_wc-orders' === $screen->id ) {
		echo '<div class="notice notice-info"><p>Hey there</p></div>';
	}
	
}

可以正常判断:

admin notices in HPOS orders WooCommerce

在第二个示例中,我们将在订单表中添加一个自定义列。列钩子使用屏幕 ID 作为钩子名称的一部分。

复制// 之前我们像这样使用 manage_edit-{POST TYPE}_columns 钩子:
// add_filter( 'manage_edit-shop_order_columns', function( $columns ){
add_filter( 'manage_woocommerce_page_wc-orders_columns', function( $columns ) {
	
	$columns[ 'misha_column' ] = 'Info from Misha';
	return $columns;
	
} );

// 之前的使用方式
// add_action( 'manage_posts_custom_column', function( $column_name, $order_id ){
add_action( 'manage_woocommerce_page_wc-orders_custom_column', function( $column, $order ){

	if( 'misha_column' === $column ) {
		echo 'hi there';
	}

}, 25, 2 );

请注意,在第二个钩子中,我们不仅更改了钩子名称,还将整个WC_Order对象作为第二个参数,因此我们可用$order->get_id()或其他参数。之前我们只有$order_id可用。

对了,这里还有一列:

WooCommerce orders HPOS create a table columns

另一个例子–我们需要为 “编辑订单 “页面创建一个自定义元数据盒子。在创建元数据盒子时,我们不能像以前那样在add_meta_box()函数中提供shop_order值。相反,我们必须使用wc_get_page_screen_id( 'shop-order' )函数。示例如下:

复制add_action( 'add_meta_boxes', function() {
	// before we used
	// add_meta_box( 'misha', 'Meta Box', 'misha_metabox', 'shop_order' );
	add_meta_box( 'misha', 'Meta Box', 'misha_metabox', wc_get_page_screen_id( 'shop-order' ) );
} );

function misha_metabox( $order ) { // WC_Order object is available here
	echo 'hey, this is an order with ID ' . $order->get_id();
}

显示效果如下:

WooCommerce orders custom meta box with HPOS support

当然,我们也可以多做一些工作,让这个功能在基于自定义文章类型的订单和基于 HPOS 的订单中都能正常工作。

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

add_action( 'add_meta_boxes', function() {

	$screen = wc_get_container()->get( CustomOrdersTableController::class )->custom_orders_table_usage_is_enabled()
		? wc_get_page_screen_id( 'shop-order' )
		: 'shop_order';
	
	add_meta_box( 'misha', 'Hey', 'misha_metabox', $screen );
	
	...

不要忘记,回调函数的主要参数是$object_id$order,这取决于 HPOS 是否激活。因此,我们还必须这样做:

复制function misha_metabox( $order_or_post_id ) {
	
	$order = ( $order_or_post_id instanceof WP_Post ) 
		? wc_get_order( $order_or_post_id->ID ) 
		: $order_or_post_id;
	
	echo 'hey, this is an order with ID ' . $order->get_id();

上面就是我们总结的 WooCommerce 高性能订单存储的方方面面,希望对大家有所帮助。如果你在使用 WooCommerce HPOS 的过程中遇到了问题,欢迎在评论中提出,我们一起讨论。

有问题可以加入网站技术QQ群一起交流学习

本站会员学习、解决问题QQ群(691961965)

客服微信号:lpf010888

Title