WooCommerce 高级教程:结合配送方式控制商品分类结账逻辑
在WooCommerce电商开发中,经常会遇到需要根据购物车内商品分类和所选配送方式,动态调整结账流程或限制购买行为的场景。比如生鲜类商品仅支持同城配送,数码类商品仅支持快递配送,这类需求就需要我们结合商品分类与配送方式做逻辑判断。本文将详细介绍如何实现这类自定义结账逻辑。
核心需求说明
我们假设需要实现以下业务规则:
- 购物车中包含"生鲜食品"分类的商品时,仅允许选择"同城配送"配送方式
- 购物车中包含"数码产品"分类的商品时,仅允许选择"快递配送"配送方式
- 如果购物车同时包含两类商品,提示用户分单结算
- 商品分类和配送方式的映射关系支持灵活配置
实现思路梳理
整个功能实现可以分为三个核心步骤:
- 遍历购物车商品,统计当前购物车包含的目标分类
- 根据包含的分类,过滤可用的配送方式列表
- 在结账页面添加对应提示信息,引导用户操作
所有逻辑都通过WooCommerce提供的钩子函数实现,不需要修改核心文件,后续升级也不会受影响。
完整代码实现
将以下代码添加到主题的functions.php文件中,或者放到自定义插件里即可生效:
<?php
/**
* 根据商品分类和配送方式映射关系,过滤可用配送方式
* @param array $rates 所有可用配送方式
* @param array $package 当前购物车包裹信息
* @return array 过滤后的配送方式
*/
function restrict_shipping_by_product_category($rates, $package) {
// 分类与配送方式映射配置,格式:分类别名 => 允许的配送方式ID
$category_shipping_map = array(
'fresh-food' => array('local_pickup'), // 生鲜食品仅允许同城配送
'digital-products' => array('flat_rate'), // 数码产品仅允许快递配送
);
// 统计购物车包含的目标分类
$has_categories = array();
foreach ($package['contents'] as $cart_item) {
$product_id = $cart_item['product_id'];
$terms = get_the_terms($product_id, 'product_cat');
if (!empty($terms) && !is_wp_error($terms)) {
foreach ($terms as $term) {
if (array_key_exists($term->slug, $category_shipping_map)) {
$has_categories[$term->slug] = true;
}
}
}
}
// 如果没有目标分类,返回所有配送方式
if (empty($has_categories)) {
return $rates;
}
// 如果同时包含多个不同映射的分类,提示分单,返回空配送方式
if (count($has_categories) > 1) {
return array();
}
// 获取当前购物车包含的分类对应的允许配送方式
$allowed_shipping_ids = array();
foreach ($has_categories as $cat_slug => $val) {
$allowed_shipping_ids = array_merge($allowed_shipping_ids, $category_shipping_map[$cat_slug]);
}
// 过滤配送方式,仅保留允许的
$filtered_rates = array();
foreach ($rates as $rate_id => $rate) {
if (in_array($rate->method_id, $allowed_shipping_ids)) {
$filtered_rates[$rate_id] = $rate;
}
}
return $filtered_rates;
}
add_filter('woocommerce_package_rates', 'restrict_shipping_by_product_category', 10, 2);
/**
* 结账页面显示分类配送规则提示
*/
function add_category_shipping_notice() {
$category_shipping_map = array(
'fresh-food' => array('local_pickup'),
'digital-products' => array('flat_rate'),
);
$has_categories = array();
foreach (WC()->cart->get_cart() as $cart_item) {
$product_id = $cart_item['product_id'];
$terms = get_the_terms($product_id, 'product_cat');
if (!empty($terms) && !is_wp_error($terms)) {
foreach ($terms as $term) {
if (array_key_exists($term->slug, $category_shipping_map)) {
$has_categories[$term->slug] = $term->name;
}
}
}
}
if (count($has_categories) > 1) {
wc_print_notice('您的购物车同时包含生鲜食品和数码产品,两类商品配送规则不同,请分开下单结算', 'error');
} elseif (count($has_categories) == 1) {
$cat_name = reset($has_categories);
if (key($has_categories) == 'fresh-food') {
wc_print_notice('您选购的生鲜食品仅支持同城配送,请选择对应配送方式', 'notice');
} elseif (key($has_categories) == 'digital-products') {
wc_print_notice('您选购的数码产品仅支持快递配送,请选择对应配送方式', 'notice');
}
}
}
add_action('woocommerce_before_checkout_form', 'add_category_shipping_notice', 10);
?>代码功能说明
上面的代码主要包含两个核心函数:
第一个函数restrict_shipping_by_product_category挂载到woocommerce_package_rates钩子,作用是过滤配送方式。首先我们定义了分类和配送方式的映射关系,你可以根据自己的需求修改$category_shipping_map数组的内容,分类别名和配送方式ID都可以在WooCommerce后台查看。然后遍历购物车所有商品,找到属于目标分类的商品,统计包含的分类。如果包含多个冲突分类,就返回空配送方式,让用户无法结算;如果只包含一个分类,就仅保留该分类允许的配送方式。
第二个函数add_category_shipping_notice挂载到woocommerce_before_checkout_form钩子,在结账表单前显示提示信息。逻辑和第一个函数类似,只是用来给用户展示友好的提示,告知用户当前购物车商品的配送规则,避免用户疑惑为什么看不到其他配送方式。
配置与调试说明
如果你需要修改分类和配送方式的映射关系,只需要调整$category_shipping_map数组即可:
- 数组的键是商品分类的别名(slug),可以在WooCommerce后台「产品-分类」页面查看
- 数组的值是该分类允许的配送方式ID,可以在WooCommerce后台「WooCommerce-设置-配送」页面,点击对应配送区域查看配送方式的ID,比如同城配送的ID通常是
local_pickup,平邮快递的ID通常是flat_rate
如果功能不生效,可以先检查购物车商品是否确实属于配置的分类,再检查配送方式ID是否正确,也可以暂时开启WordPress的调试模式查看是否有错误输出。
扩展思路
这个逻辑还可以做很多扩展,比如:
- 增加更多分类和配送方式的映射规则
- 如果是虚拟商品,直接隐藏所有配送方式
- 根据购物车商品总重量动态调整允许的配送方式
- 在商品详情页就提示用户该商品的配送限制
只需要基于上面的代码结构,补充对应的判断逻辑即可,核心思路都是先获取购物车信息,再根据规则过滤或提示。