WordPress自定义WooCommerce插件添加新产品时无法设置价格
问题排查:新增WooCommerce Metal产品类型时价格为空的问题
我正在开发一款扩展WooCommerce的WordPress插件,新增了“Metal”产品类型及对应必填自定义字段。使用woocommerce_process_product_meta钩子保存元值,但仅在添加新产品时产品价格被设置为空字符串,更新产品时功能完全正常。相关代码如下:
add_action( 'woocommerce_process_product_meta', 'wm_save_fields' ); function wm_save_fields( $id ){ if(isset($_POST['product-type']) && $_POST['product-type'] == 'metal') { $weight = $_POST['_metal_weight']; $margin = $_POST['_metal_margin']; if( !isset($weight) || !isset($margin) ) { return; } $weight = ($weight == "") ? 0 : sanitize_text_field($_POST['_metal_weight']); $margin = ($margin == "") ? 0 : sanitize_text_field($_POST['_metal_margin']); $gold_price = get_option('wm_gold_price'); $price = $weight * $gold_price; $margin_price = $price + (($margin / 100) * $price); $margin_price = number_format($margin_price, 2); update_post_meta( $id, 'wm_weight', $weight ); update_post_meta( $id, 'wm_margin', $margin ); if(get_post_meta( $id, '_price' )) { update_post_meta( $id, '_price', $margin_price ); } else { add_post_meta( $id, '_price', $margin_price ); } } }
核心原因分析
- 钩子执行时机冲突:
woocommerce_process_product_meta的默认执行优先级是10,而WooCommerce在创建新产品时,会默认将_price字段设为空值,这个操作的执行时机可能晚于你的代码,直接覆盖了你设置的价格。 get_post_meta判断逻辑错误:get_post_meta($id, '_price')在新产品创建时会返回空数组(元数据不存在),但PHP中空数组在布尔判断中会被视为true,导致代码执行update_post_meta,但后续WooCommerce的默认逻辑又把价格清空。- 价格格式兼容性问题:
number_format返回带千分位的字符串(如1,234.56),而WooCommerce的价格字段需要浮点型或无千分位的字符串,可能导致识别异常。
修复方案
方案1:更换钩子,提前执行逻辑
使用woocommerce_new_product(创建新产品触发)和woocommerce_update_product(更新产品触发)两个钩子,确保你的代码在WooCommerce默认设置价格之前执行:
// 处理新产品创建 add_action( 'woocommerce_new_product', 'wm_save_fields_new', 10, 1 ); function wm_save_fields_new( $id ){ wm_save_product_fields( $id ); } // 处理产品更新 add_action( 'woocommerce_update_product', 'wm_save_fields_update', 10, 1 ); function wm_save_fields_update( $id ){ wm_save_product_fields( $id ); } // 通用处理函数 function wm_save_product_fields( $id ){ if( isset($_POST['product-type']) && $_POST['product-type'] == 'metal' ) { // 严谨判断字段是否存在 $weight = isset($_POST['_metal_weight']) ? $_POST['_metal_weight'] : ''; $margin = isset($_POST['_metal_margin']) ? $_POST['_metal_margin'] : ''; // 必填字段校验 if( empty($weight) || empty($margin) ) { return; } // 清理并转换为数值类型 $weight = floatval( sanitize_text_field($weight) ); $margin = floatval( sanitize_text_field($margin) ); $gold_price = floatval( get_option('wm_gold_price', 0) ); // 计算价格,用round避免千分位问题 $price = $weight * $gold_price; $margin_price = $price + (($margin / 100) * $price); $margin_price = round($margin_price, 2); // 保存自定义元数据 update_post_meta( $id, 'wm_weight', $weight ); update_post_meta( $id, 'wm_margin', $margin ); // 直接更新价格,update_post_meta自动处理添加/更新 update_post_meta( $id, '_price', $margin_price ); // 同步更新_regular_price,保持WooCommerce数据一致性 update_post_meta( $id, '_regular_price', $margin_price ); } }
方案2:提升原钩子的执行优先级
如果坚持使用woocommerce_process_product_meta,将优先级设为5(早于默认的10),确保代码先于WooCommerce的默认逻辑执行:
add_action( 'woocommerce_process_product_meta', 'wm_save_fields', 5, 1 ); function wm_save_fields( $id ){ if(isset($_POST['product-type']) && $_POST['product-type'] == 'metal') { $weight = isset($_POST['_metal_weight']) ? sanitize_text_field($_POST['_metal_weight']) : 0; $margin = isset($_POST['_metal_margin']) ? sanitize_text_field($_POST['_metal_margin']) : 0; $weight = floatval($weight); $margin = floatval($margin); $gold_price = floatval( get_option('wm_gold_price', 0) ); $price = $weight * $gold_price; $margin_price = $price + (($margin / 100) * $price); $margin_price = round($margin_price, 2); update_post_meta( $id, 'wm_weight', $weight ); update_post_meta( $id, 'wm_margin', $margin ); // 直接更新价格,无需判断是否存在 update_post_meta( $id, '_price', $margin_price ); update_post_meta( $id, '_regular_price', $margin_price ); } }
内容的提问来源于stack exchange,提问作者howtogeek24
相关产品推荐
相关产品推荐

