133 lines
5.3 KiB
PHP
133 lines
5.3 KiB
PHP
<?php
|
||
/**
|
||
* 前端:渲染混装产品的自定义 add-to-cart 表单、校验并加入购物车。
|
||
*/
|
||
defined('ABSPATH') || exit;
|
||
|
||
class Yoone_Product_Bundles_Frontend {
|
||
protected static $instance = null;
|
||
|
||
public static function instance() {
|
||
if (null === self::$instance) self::$instance = new self();
|
||
return self::$instance;
|
||
}
|
||
|
||
private function __construct() {
|
||
// 用我们的模板替换默认的 add-to-cart 按钮
|
||
add_action('woocommerce_' . Yoone_Product_Bundles::TYPE . '_add_to_cart', array($this, 'render_add_to_cart')); // 显示表单
|
||
|
||
// 处理加入购物车:校验数量并打包所选组件数据
|
||
add_filter('woocommerce_add_to_cart_validation', array($this, 'validate_add_to_cart'), 10, 6);
|
||
add_filter('woocommerce_add_cart_item_data', array($this, 'add_cart_item_data'), 10, 3);
|
||
|
||
// 在购物车/订单中显示组件摘要
|
||
add_filter('woocommerce_get_item_data', array($this, 'display_cart_item_data'), 10, 2);
|
||
|
||
// 结算前动态调整价格
|
||
add_action('woocommerce_before_calculate_totals', array($this, 'adjust_bundle_price'), 20, 1);
|
||
}
|
||
|
||
/**
|
||
* 渲染自定义表单:按所选分类分组显示允许的简单商品,提供数量输入。
|
||
*/
|
||
public function render_add_to_cart() {
|
||
wc_get_template('single-product/add-to-cart/yoone-bundle.php', array(), '', YOONE_PB_PATH . 'templates/');
|
||
}
|
||
|
||
/**
|
||
* 校验:总选择数量 >= min_qty
|
||
*/
|
||
public function validate_add_to_cart($passed, $product_id, $quantity, $variation_id, $variations, $cart_item_data) {
|
||
$product = wc_get_product($product_id);
|
||
if (! $product || $product->get_type() !== Yoone_Product_Bundles::TYPE) return $passed;
|
||
|
||
$config = Yoone_Product_Bundles::get_bundle_config($product);
|
||
$min = max(0, absint($config['min_qty']));
|
||
|
||
// 从 POST 中收集数量
|
||
$selected = isset($_POST['yoone_bundle_components']) ? (array) $_POST['yoone_bundle_components'] : array();
|
||
$total_qty = 0;
|
||
foreach ($selected as $pid => $qty) {
|
||
$qty = absint($qty);
|
||
if ($qty > 0) $total_qty += $qty;
|
||
}
|
||
|
||
if ($total_qty < $min) {
|
||
wc_add_notice(sprintf(__('请至少选择 %d 件混装组件后再加入购物车。', 'yoone-product-bundles'), $min), 'error');
|
||
return false;
|
||
}
|
||
return $passed;
|
||
}
|
||
|
||
/**
|
||
* 将所选组件数据存入购物车项。
|
||
*/
|
||
public function add_cart_item_data($cart_item_data, $product_id, $variation_id) {
|
||
$product = wc_get_product($product_id);
|
||
if (! $product || $product->get_type() !== Yoone_Product_Bundles::TYPE) return $cart_item_data;
|
||
|
||
$selected = isset($_POST['yoone_bundle_components']) ? (array) $_POST['yoone_bundle_components'] : array();
|
||
// 仅保留大于 0 的数量
|
||
$components = array();
|
||
foreach ($selected as $pid => $qty) {
|
||
$pid = absint($pid);
|
||
$qty = absint($qty);
|
||
if ($pid && $qty > 0) $components[$pid] = $qty;
|
||
}
|
||
if (! empty($components)) {
|
||
$cart_item_data['yoone_bundle_components'] = $components;
|
||
}
|
||
return $cart_item_data;
|
||
}
|
||
|
||
/**
|
||
* 在购物车行项目中展示所选组件摘要。
|
||
*/
|
||
public function display_cart_item_data($item_data, $cart_item) {
|
||
if (! empty($cart_item['yoone_bundle_components'])) {
|
||
$lines = array();
|
||
foreach ($cart_item['yoone_bundle_components'] as $pid => $qty) {
|
||
$p = wc_get_product($pid);
|
||
if ($p) {
|
||
$lines[] = sprintf('%s × %d', $p->get_name(), $qty);
|
||
}
|
||
}
|
||
if (! empty($lines)) {
|
||
$item_data[] = array(
|
||
'key' => __('混装内容', 'yoone-product-bundles'),
|
||
'value' => implode("\n", $lines),
|
||
'display' => implode('<br/>', array_map('esc_html', $lines)),
|
||
);
|
||
}
|
||
}
|
||
return $item_data;
|
||
}
|
||
|
||
/**
|
||
* 结算前根据组件动态调整混装产品的行项目价格(= 所选简单商品单价 × 数量 的总和)。
|
||
* 注意:这里使用的是产品当前价格(不含税的基础价),是否包含税由 WooCommerce 税设置决定。
|
||
*/
|
||
public function adjust_bundle_price($cart) {
|
||
if (is_admin() && ! defined('DOING_AJAX')) {
|
||
return; // 后台不处理
|
||
}
|
||
if (empty($cart) || ! method_exists($cart, 'get_cart')) return;
|
||
|
||
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
|
||
if (empty($cart_item['yoone_bundle_components'])) continue;
|
||
$product = isset($cart_item['data']) ? $cart_item['data'] : null;
|
||
if (! $product || ! is_a($product, 'WC_Product')) continue;
|
||
if ($product->get_type() !== Yoone_Product_Bundles::TYPE) continue;
|
||
|
||
$total = 0.0;
|
||
foreach ($cart_item['yoone_bundle_components'] as $pid => $qty) {
|
||
$p = wc_get_product($pid);
|
||
if (! $p) continue;
|
||
$unit = floatval($p->get_price()); // 基础价
|
||
$total += $unit * absint($qty);
|
||
}
|
||
// 将产品行项目价格设为总价
|
||
$product->set_price($total);
|
||
}
|
||
}
|
||
} |