检查客户是否已经在 WooCommerce 中购买了东西
Checking if customer has already bought something in WooCommerce
我想创建一个 WooCommerce 插件来为客户(有购买历史记录)添加一些优惠。
如何查看用户之前购买过的东西?
谢谢。
Update 2020:
Yes it is possible creating a conditional function that return true
when a customer has already at least one order with status completed.
这里是这个条件函数的代码:
function has_bought() {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => 1, // one order is enough
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed', // Only orders with "completed" status
'fields' => 'ids', // Return Ids "completed"
) );
// return "true" when customer has already at least one order (false if not)
return count($customer_orders) > 0 ? true : false;
}
此代码已经过测试并且有效。
此代码在您的活动子主题或主题的 function.php 文件中,或在插件 php 文件中。
用法(作为条件):
- 您可以在之前复制到活动子主题或主题的WooCommerce templates中使用它。
- 在您的主题 php 文件中。
- 在插件 php 文件中。
- 任意 php 函数或 WordPress/WooCommerce.
参考资料
简化版:
function has_bought() {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed' // Only orders with status "completed"
) );
// return "true" when customer has already one order
return count( $customer_orders ) > 0 ? true : false;
}
2021 UPDATE - Handling guests "billing email" - Improved and secured SQL query
这是一个 更轻便、更快的条件函数 如果客户已经购买,它将 return 为真。
它处理来自用户 ID 的注册用户访客和来自账单电子邮件的访客:
- 对于注册用户:如果可选参数没有设置用户 ID,将使用当前用户 ID。
- 对于客人:帐单电子邮件将需要作为函数中的参数。
这个更轻巧和改进的函数 (部分基于 wc_customer_bought_product()
函数源代码) 将 return 一个基于订单计数的布尔值 (O订单为false,至少有一个已付款订单为true):
function has_bought( $value = 0 ) {
if ( ! is_user_logged_in() && $value === 0 ) {
return false;
}
global $wpdb;
// Based on user ID (registered users)
if ( is_numeric( $value) ) {
$meta_key = '_customer_user';
$meta_value = $value == 0 ? (int) get_current_user_id() : (int) $value;
}
// Based on billing email (Guest users)
else {
$meta_key = '_billing_email';
$meta_value = sanitize_email( $value );
}
$paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$count = $wpdb->get_var( $wpdb->prepare("
SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )
AND p.post_type LIKE 'shop_order'
AND pm.meta_key = '%s'
AND pm.meta_value = %s
LIMIT 1
", $meta_key, $meta_value ) );
// Return a boolean value based on orders count
return $count > 0;
}
代码进入您的活动子主题(或主题)的 functions.php 文件或任何插件文件。
此代码已在 Woocommerce 3+ 上测试并有效(它应该也适用于以前的版本)。
For multiple products:
用法示例1 (登录客户)
if( has_bought() )
echo '<p>You have already made a purchase</p>';
else
echo '<p>Welcome, for your first purchase you will get a discount of 10%</p>';
使用示例2 (设置用户ID)
// Define the user ID
$user_id = 85;
if( has_bought( $user_id ) )
echo '<p>customer have already made a purchase</p>';
else
echo '<p>Customer with 0 purchases</p>';
如果$user_id
未定义且当前用户未登录,此函数将return false
.
使用示例 3 - 客人 (设置账单邮箱)
// Define the billing email (string)
$email = 'louis.fourteen@gmail.com';
if( has_bought( $email ) )
echo '<p>customer have already made a purchase</p>';
else
echo '<p>Customer with 0 purchases</p>'
The code of this function is partially based on the built in WooCommerce function wc_customer_bought_product()
source code.
如果启用了来宾检查功能,那么此功能可能会有所帮助
只需发送 $customer_email 作为参数,函数将检查该电子邮件的所有订单,return true 或 false。
function has_bought($customer_email){
$orders = get_posts(array(
'numberposts' => -1,
'post_type' => 'shop_order',
'post_status' => array('wc-processing', 'wc-completed'),
));
$email_array = array();
foreach($orders as $order) {
$order_obj = wc_get_order($order->ID);
$order_obj_data = $order_obj->get_data();
array_push($email_array, $order_obj_data['billing']['email']);
}
if (in_array($customer_email, $email_array)) {
return true;
} else {
return false;
}
}
带有付费状态或状态不可知查询的增强版本,还改进了一些代码输入,适用于 PHP 5.4+ :
function has_bought(int $user_id = 0, bool $paid = true ) {
global $wpdb;
$user_id = (empty($user_id)) ? get_current_user_id() : $user_id;
$sql_str = "
SELECT p.ID FROM ".$wpdb->posts." AS p
INNER JOIN
".$wpdb->postmeta." AS pm ON p.ID = pm.post_id
WHERE
p.post_type LIKE 'shop_order'
AND pm.meta_key = '_customer_user'
AND pm.meta_value = %d
";
$args = [(int) $user_id];
if ($paid === true) {
$paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$sql_str .= "AND p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )";
}
$sql = $wpdb->prepare($sql_str, $args);
return (bool) $wpdb->get_var( $sql );
}
从@Antonio Novak 的简化版本开始,我做了一些改进,以覆盖所有可能表明用户已经购买的订单状态。
状态列表
WooCommerce 提供了有关状态的详细文档。检查一下:https://docs.woocommerce.com/document/managing-orders/
但是,我在这里使用 slugs 转录列表:
$order_statuses = array(
'wc-pending' => _x( 'Pending payment', 'Order status', 'woocommerce' ),
'wc-processing' => _x( 'Processing', 'Order status', 'woocommerce' ),
'wc-on-hold' => _x( 'On hold', 'Order status', 'woocommerce' ),
'wc-completed' => _x( 'Completed', 'Order status', 'woocommerce' ),
'wc-cancelled' => _x( 'Cancelled', 'Order status', 'woocommerce' ),
'wc-refunded' => _x( 'Refunded', 'Order status', 'woocommerce' ),
'wc-failed' => _x( 'Failed', 'Order status', 'woocommerce' ),
);
此外,我决定添加 wc-shipped 状态。当然,是自定义状态,但通常用于电子商务,对吗?
我没有问我用户是否已经购买,假设最大的兴趣可能是提供更多激励来赢得新用户,我冒昧地颠倒了问题和函数的答案。
使用建议的功能时,return 在用户首次购买时为真,在查询中列出的状态下已经有订单时为假。
此外,我还将用户 ID 参数设为可选,因此该函数可以在多种情况下重复使用,而不仅仅是在用户会话中。
函数
function is_the_first_purchase( $user_id = 0) {
if( $user_id == 0 ){
$user_id = get_current_user_id();
}
if( ! $user_id ){
return true;
}
$customer_orders = get_posts( array(
'numberposts' => -1,
'post_type' => 'shop_order',
'post_status' => array(
'wc-completed',
'wc-on-hold',
'wc-processing',
'wc-shipped',
'wc-refunded'
),
'meta_query' => array(
array(
'key' => '_customer_user',
'meta_value' => $user_id,
)
)
));
// return "true" when customer
// does not have an completed order
return count( $customer_orders ) > 0 ? false : true;
}
带有缓存层的版本(使用临时 API 键,一周长)。未登录用户 returns false。
function has_bought() {
if(!is_user_logged_in()) return false;
$transient = 'has_bought_'.get_current_user_id();
$has_bought = get_transient($transient);
if(!$has_bought) {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => 1, // one order is enough
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed', // Only orders with "completed" status
'fields' => 'ids', // Return Ids "completed"
) );
$has_bought = count($customer_orders) > 0 ? true : false;
set_transient($transient, $has_bought, WEEK_IN_SECONDS);
}
// return "true" when customer has already at least one order (false if not)
return $has_bought;
}
您可以使用内置的 woocommerce 订单查询来查询 email/phone/etc...
$args = [
//'customer_id' => $user_id, // can used for query by users
'billing_phone' => $phone, // you can also query by 'billing_email', 'billing_first_name' and etc.
'status' => ['wc-processing', 'wc-completed'],
'limit' => -1,
];
$query = new WC_Order_Query($args);
$orders = $query->get_orders();
return count($orders) > 1 ? true : false;
WC_Order_Query 的更多详细信息可在此处找到:
https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
这个解决方案很简单,因为它使用 WooCommerce 内部 wc_get_orders
函数来获取同一客户的订单。
而且速度很快,因为我们不会检索整个订单对象,而只会检索订单 ID。 (如果不需要订单对象,只检索 ID 会快很多)
此外,我们限制只能检索 1 个订单,而不是所有订单。这意味着一旦找到一个现有的已付款订单,查询就会停止。这就是我们确定这是否是现有客户所需的全部内容。
// returns true or false
function is_existing_customer( $order ) {
$query_arguments = [
'return' => 'ids',
'post_status' => wc_get_is_paid_statuses(),
'limit' => 1,
];
// If the user is looged in we try to fetch all his orders
if (is_user_logged_in()) {
$current_user = wp_get_current_user();
$query_arguments['customer'] = $current_user->user_email;
} else { // Otherwise we use the billing email to fetch all orders
$query_arguments['billing_email'] = $order->get_billing_email();
}
$orders = wc_get_orders($query_arguments);
return count($orders) > 0;
}
您可以将此 one-liner 用于 登录 用户:
$has_bought_before = wc_get_customer_order_count( get_current_user_id() ) >= 1 ? true : false;
WordPress/WooCommerce 使用的函数:
- wc_get_customer_order_count() – 按客户获取订单总数。
- get_current_user_id() - 获取当前用户的ID
我想创建一个 WooCommerce 插件来为客户(有购买历史记录)添加一些优惠。
如何查看用户之前购买过的东西?
谢谢。
Update 2020:
Yes it is possible creating a conditional function that return
true
when a customer has already at least one order with status completed.
这里是这个条件函数的代码:
function has_bought() {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => 1, // one order is enough
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed', // Only orders with "completed" status
'fields' => 'ids', // Return Ids "completed"
) );
// return "true" when customer has already at least one order (false if not)
return count($customer_orders) > 0 ? true : false;
}
此代码已经过测试并且有效。
此代码在您的活动子主题或主题的 function.php 文件中,或在插件 php 文件中。
用法(作为条件):
- 您可以在之前复制到活动子主题或主题的WooCommerce templates中使用它。
- 在您的主题 php 文件中。
- 在插件 php 文件中。
- 任意 php 函数或 WordPress/WooCommerce.
参考资料
简化版:
function has_bought() {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed' // Only orders with status "completed"
) );
// return "true" when customer has already one order
return count( $customer_orders ) > 0 ? true : false;
}
2021 UPDATE - Handling guests "billing email" - Improved and secured SQL query
这是一个 更轻便、更快的条件函数 如果客户已经购买,它将 return 为真。
它处理来自用户 ID 的注册用户访客和来自账单电子邮件的访客:
- 对于注册用户:如果可选参数没有设置用户 ID,将使用当前用户 ID。
- 对于客人:帐单电子邮件将需要作为函数中的参数。
这个更轻巧和改进的函数 (部分基于 wc_customer_bought_product()
函数源代码) 将 return 一个基于订单计数的布尔值 (O订单为false,至少有一个已付款订单为true):
function has_bought( $value = 0 ) {
if ( ! is_user_logged_in() && $value === 0 ) {
return false;
}
global $wpdb;
// Based on user ID (registered users)
if ( is_numeric( $value) ) {
$meta_key = '_customer_user';
$meta_value = $value == 0 ? (int) get_current_user_id() : (int) $value;
}
// Based on billing email (Guest users)
else {
$meta_key = '_billing_email';
$meta_value = sanitize_email( $value );
}
$paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$count = $wpdb->get_var( $wpdb->prepare("
SELECT COUNT(p.ID) FROM {$wpdb->prefix}posts AS p
INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )
AND p.post_type LIKE 'shop_order'
AND pm.meta_key = '%s'
AND pm.meta_value = %s
LIMIT 1
", $meta_key, $meta_value ) );
// Return a boolean value based on orders count
return $count > 0;
}
代码进入您的活动子主题(或主题)的 functions.php 文件或任何插件文件。
此代码已在 Woocommerce 3+ 上测试并有效(它应该也适用于以前的版本)。
For multiple products:
用法示例1 (登录客户)
if( has_bought() )
echo '<p>You have already made a purchase</p>';
else
echo '<p>Welcome, for your first purchase you will get a discount of 10%</p>';
使用示例2 (设置用户ID)
// Define the user ID
$user_id = 85;
if( has_bought( $user_id ) )
echo '<p>customer have already made a purchase</p>';
else
echo '<p>Customer with 0 purchases</p>';
如果$user_id
未定义且当前用户未登录,此函数将return false
.
使用示例 3 - 客人 (设置账单邮箱)
// Define the billing email (string)
$email = 'louis.fourteen@gmail.com';
if( has_bought( $email ) )
echo '<p>customer have already made a purchase</p>';
else
echo '<p>Customer with 0 purchases</p>'
The code of this function is partially based on the built in WooCommerce function
wc_customer_bought_product()
source code.
如果启用了来宾检查功能,那么此功能可能会有所帮助
只需发送 $customer_email 作为参数,函数将检查该电子邮件的所有订单,return true 或 false。
function has_bought($customer_email){
$orders = get_posts(array(
'numberposts' => -1,
'post_type' => 'shop_order',
'post_status' => array('wc-processing', 'wc-completed'),
));
$email_array = array();
foreach($orders as $order) {
$order_obj = wc_get_order($order->ID);
$order_obj_data = $order_obj->get_data();
array_push($email_array, $order_obj_data['billing']['email']);
}
if (in_array($customer_email, $email_array)) {
return true;
} else {
return false;
}
}
带有付费状态或状态不可知查询的增强版本,还改进了一些代码输入,适用于 PHP 5.4+ :
function has_bought(int $user_id = 0, bool $paid = true ) {
global $wpdb;
$user_id = (empty($user_id)) ? get_current_user_id() : $user_id;
$sql_str = "
SELECT p.ID FROM ".$wpdb->posts." AS p
INNER JOIN
".$wpdb->postmeta." AS pm ON p.ID = pm.post_id
WHERE
p.post_type LIKE 'shop_order'
AND pm.meta_key = '_customer_user'
AND pm.meta_value = %d
";
$args = [(int) $user_id];
if ($paid === true) {
$paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
$sql_str .= "AND p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )";
}
$sql = $wpdb->prepare($sql_str, $args);
return (bool) $wpdb->get_var( $sql );
}
从@Antonio Novak 的简化版本开始,我做了一些改进,以覆盖所有可能表明用户已经购买的订单状态。
状态列表
WooCommerce 提供了有关状态的详细文档。检查一下:https://docs.woocommerce.com/document/managing-orders/
但是,我在这里使用 slugs 转录列表:
$order_statuses = array(
'wc-pending' => _x( 'Pending payment', 'Order status', 'woocommerce' ),
'wc-processing' => _x( 'Processing', 'Order status', 'woocommerce' ),
'wc-on-hold' => _x( 'On hold', 'Order status', 'woocommerce' ),
'wc-completed' => _x( 'Completed', 'Order status', 'woocommerce' ),
'wc-cancelled' => _x( 'Cancelled', 'Order status', 'woocommerce' ),
'wc-refunded' => _x( 'Refunded', 'Order status', 'woocommerce' ),
'wc-failed' => _x( 'Failed', 'Order status', 'woocommerce' ),
);
此外,我决定添加 wc-shipped 状态。当然,是自定义状态,但通常用于电子商务,对吗?
我没有问我用户是否已经购买,假设最大的兴趣可能是提供更多激励来赢得新用户,我冒昧地颠倒了问题和函数的答案。
使用建议的功能时,return 在用户首次购买时为真,在查询中列出的状态下已经有订单时为假。
此外,我还将用户 ID 参数设为可选,因此该函数可以在多种情况下重复使用,而不仅仅是在用户会话中。
函数
function is_the_first_purchase( $user_id = 0) {
if( $user_id == 0 ){
$user_id = get_current_user_id();
}
if( ! $user_id ){
return true;
}
$customer_orders = get_posts( array(
'numberposts' => -1,
'post_type' => 'shop_order',
'post_status' => array(
'wc-completed',
'wc-on-hold',
'wc-processing',
'wc-shipped',
'wc-refunded'
),
'meta_query' => array(
array(
'key' => '_customer_user',
'meta_value' => $user_id,
)
)
));
// return "true" when customer
// does not have an completed order
return count( $customer_orders ) > 0 ? false : true;
}
带有缓存层的版本(使用临时 API 键,一周长)。未登录用户 returns false。
function has_bought() {
if(!is_user_logged_in()) return false;
$transient = 'has_bought_'.get_current_user_id();
$has_bought = get_transient($transient);
if(!$has_bought) {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => 1, // one order is enough
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed', // Only orders with "completed" status
'fields' => 'ids', // Return Ids "completed"
) );
$has_bought = count($customer_orders) > 0 ? true : false;
set_transient($transient, $has_bought, WEEK_IN_SECONDS);
}
// return "true" when customer has already at least one order (false if not)
return $has_bought;
}
您可以使用内置的 woocommerce 订单查询来查询 email/phone/etc...
$args = [
//'customer_id' => $user_id, // can used for query by users
'billing_phone' => $phone, // you can also query by 'billing_email', 'billing_first_name' and etc.
'status' => ['wc-processing', 'wc-completed'],
'limit' => -1,
];
$query = new WC_Order_Query($args);
$orders = $query->get_orders();
return count($orders) > 1 ? true : false;
WC_Order_Query 的更多详细信息可在此处找到: https://github.com/woocommerce/woocommerce/wiki/wc_get_orders-and-WC_Order_Query
这个解决方案很简单,因为它使用 WooCommerce 内部 wc_get_orders
函数来获取同一客户的订单。
而且速度很快,因为我们不会检索整个订单对象,而只会检索订单 ID。 (如果不需要订单对象,只检索 ID 会快很多)
此外,我们限制只能检索 1 个订单,而不是所有订单。这意味着一旦找到一个现有的已付款订单,查询就会停止。这就是我们确定这是否是现有客户所需的全部内容。
// returns true or false
function is_existing_customer( $order ) {
$query_arguments = [
'return' => 'ids',
'post_status' => wc_get_is_paid_statuses(),
'limit' => 1,
];
// If the user is looged in we try to fetch all his orders
if (is_user_logged_in()) {
$current_user = wp_get_current_user();
$query_arguments['customer'] = $current_user->user_email;
} else { // Otherwise we use the billing email to fetch all orders
$query_arguments['billing_email'] = $order->get_billing_email();
}
$orders = wc_get_orders($query_arguments);
return count($orders) > 0;
}
您可以将此 one-liner 用于 登录 用户:
$has_bought_before = wc_get_customer_order_count( get_current_user_id() ) >= 1 ? true : false;
WordPress/WooCommerce 使用的函数:
- wc_get_customer_order_count() – 按客户获取订单总数。
- get_current_user_id() - 获取当前用户的ID