183 lines
9.3 KiB
PHP
183 lines
9.3 KiB
PHP
<?php
|
||
/**
|
||
* Yoone Product Bundles - Mix and Match Product Selection Form
|
||
*
|
||
* This template part contains the full frontend interface for selecting bundle components,
|
||
* including the card-based layout, quantity inputs, and dynamically updated total count.
|
||
* It is hooked into `woocommerce_single_product_summary` to replace the standard add-to-cart form.
|
||
*
|
||
* @version 3.0.1
|
||
*/
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
global $product;
|
||
|
||
// --- 数据准备逻辑 ---
|
||
$config = Yoone_Product_Bundles::get_bundle_config($product);
|
||
$allowed = $config['allowed_products'];
|
||
$select_mode = isset($config['select_mode']) ? $config['select_mode'] : 'include';
|
||
$min_qty = max(0, absint($config['min_qty']));
|
||
// 是否允许在购物车中编辑混装
|
||
$edit_in_cart_enabled = isset($config['edit_in_cart']) && $config['edit_in_cart'] === 'yes';
|
||
// 折扣设置
|
||
$discount_type = isset($config['discount_type']) ? $config['discount_type'] : 'none';
|
||
$discount_amount = isset($config['discount_amount']) ? floatval($config['discount_amount']) : 0.0;
|
||
// 价格显示格式(用于前端计算与显示)
|
||
$currency_symbol = get_woocommerce_currency_symbol();
|
||
$price_decimals = wc_get_price_decimals();
|
||
$decimal_separator = wc_get_price_decimal_separator();
|
||
$thousand_separator = wc_get_price_thousand_separator();
|
||
// 读取按 taxonomy 分组的配置(支持 product_cat / product_tag)
|
||
$group_taxonomy = isset($config['group_taxonomy']) ? $config['group_taxonomy'] : 'product_cat';
|
||
$term_ids = isset($config['group_terms']) ? (array) $config['group_terms'] : array();
|
||
|
||
$allowed_products = array();
|
||
foreach ($allowed as $pid) {
|
||
$p = wc_get_product($pid);
|
||
if ($p && $p->is_type('simple')) {
|
||
$allowed_products[$pid] = $p;
|
||
}
|
||
}
|
||
// 兜底:如果为 exclude/all 模式,但由于缓存或其他原因导致 allowed_products 为空,则直接读取所有 simple 产品
|
||
if (empty($allowed_products) && in_array($select_mode, array('exclude','all'), true)) {
|
||
$all_ids = wc_get_products(array(
|
||
'type' => array('simple'),
|
||
'status' => array('publish'),
|
||
'catalog_visibility' => 'any',
|
||
'limit' => -1,
|
||
'return' => 'ids',
|
||
));
|
||
if (is_array($all_ids)) {
|
||
foreach ($all_ids as $pid) {
|
||
$p = wc_get_product($pid);
|
||
if ($p && $p->is_type('simple')) {
|
||
$allowed_products[$pid] = $p;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
$groups = array();
|
||
if (!empty($term_ids)) {
|
||
$is_grouped = true;
|
||
$others = array();
|
||
foreach ($allowed_products as $pid => $p) {
|
||
$terms = get_the_terms($pid, $group_taxonomy);
|
||
$matched = false;
|
||
if (is_array($terms)) {
|
||
foreach ($terms as $t) {
|
||
if (in_array($t->term_id, $term_ids, true)) {
|
||
if (!isset($groups[$t->term_id])) {
|
||
$groups[$t->term_id] = array('term' => $t, 'items' => array());
|
||
}
|
||
$groups[$t->term_id]['items'][] = $p;
|
||
$matched = true;
|
||
}
|
||
}
|
||
}
|
||
if (!$matched) $others[] = $p;
|
||
}
|
||
if (!empty($others)) $groups[0] = array('term' => null, 'items' => $others);
|
||
} else {
|
||
$is_grouped = false;
|
||
$groups[0] = array('term' => null, 'items' => array_values($allowed_products));
|
||
}
|
||
// --- 数据准备逻辑结束 ---
|
||
?>
|
||
|
||
<div class="yoone-bundle-form-container">
|
||
<?php do_action('woocommerce_before_add_to_cart_form'); ?>
|
||
<form class="cart yoone-bundle-form" action="<?php echo esc_url(apply_filters('woocommerce_add_to_cart_form_action', $product->get_permalink())); ?>" method="post" enctype="multipart/form-data"
|
||
data-currency-symbol="<?php echo esc_attr($currency_symbol); ?>"
|
||
data-price-decimals="<?php echo esc_attr($price_decimals); ?>"
|
||
data-decimal-separator="<?php echo esc_attr($decimal_separator); ?>"
|
||
data-thousand-separator="<?php echo esc_attr($thousand_separator); ?>"
|
||
data-discount-type="<?php echo esc_attr($discount_type); ?>"
|
||
data-discount-amount="<?php echo esc_attr($discount_amount); ?>"
|
||
>
|
||
<div class="yoone-bundle-meta">
|
||
<p class="yoone-bundle-min"><?php printf(esc_html__('Select at least %d items to build your bundle.', 'yoone-product-bundles'), $min_qty); ?></p>
|
||
<p class="yoone-bundle-selected"><?php esc_html_e('Currently selected:', 'yoone-product-bundles'); ?> <span class="yoone-bundle-selected-count">0</span></p>
|
||
<?php if ($edit_in_cart_enabled) : ?>
|
||
<div class="yoone-bundle-edit-in-cart">
|
||
<label>
|
||
<input type="checkbox" name="yoone_bundle_edit_in_cart" value="1" checked />
|
||
<?php esc_html_e('Allow editing bundle items in Cart (quantities and removal).', 'yoone-product-bundles'); ?>
|
||
</label>
|
||
</div>
|
||
<?php endif; ?>
|
||
<div class="yoone-bundle-totals">
|
||
<p class="yoone-bundle-total-raw">
|
||
<?php esc_html_e('Selected items total:', 'yoone-product-bundles'); ?>
|
||
<span class="yoone-bundle-total-raw-value">0</span>
|
||
</p>
|
||
<?php if ($discount_type !== 'none' && $discount_amount > 0) : ?>
|
||
<p class="yoone-bundle-total-discounted">
|
||
<?php esc_html_e('After bundle discount:', 'yoone-product-bundles'); ?>
|
||
<span class="yoone-bundle-total-discounted-value">0</span>
|
||
</p>
|
||
<?php endif; ?>
|
||
</div>
|
||
<div class="yoone-bundle-actions">
|
||
<input type="hidden" name="add-to-cart" value="<?php echo esc_attr($product->get_id()); ?>" />
|
||
<?php
|
||
// 在按钮之前触发钩子,方便订阅等插件在此渲染选项
|
||
do_action('woocommerce_before_add_to_cart_button');
|
||
?>
|
||
<button type="submit" class="single_add_to_cart_button button alt" disabled><?php echo esc_html( apply_filters( 'woocommerce_product_single_add_to_cart_text', __( 'Add to Cart', 'yoone-product-bundles' ) ) ); ?></button>
|
||
<?php
|
||
// 在按钮之后触发钩子,保持与 Woo 默认模板一致
|
||
do_action('woocommerce_after_add_to_cart_button');
|
||
?>
|
||
</div>
|
||
</div>
|
||
<?php if (empty($allowed_products)) : ?>
|
||
<p><?php esc_html_e('No products are available for this bundle. Please configure it in the backend.', 'yoone-product-bundles'); ?></p>
|
||
<?php else : ?>
|
||
<?php foreach ($groups as $gid => $group) : ?>
|
||
<div class="yoone-bundle-group">
|
||
<?php if (!empty($group['term'])) : ?>
|
||
<h3 class="yoone-bundle-group-title"><?php echo esc_html($group['term']->name); ?></h3>
|
||
<?php else : ?>
|
||
<h3 class="yoone-bundle-group-title">
|
||
<?php echo $is_grouped
|
||
? esc_html__('Others', 'yoone-product-bundles')
|
||
: esc_html__('All Products', 'yoone-product-bundles'); ?>
|
||
</h3>
|
||
<?php endif; ?>
|
||
|
||
<?php if (empty($group['items'])) : ?>
|
||
<p class="yoone-bundle-no-items"><?php esc_html_e('No products available in this group.', 'yoone-product-bundles'); ?></p>
|
||
<?php else : ?>
|
||
<div class="yoone-bundle-items-wrapper">
|
||
<?php foreach ($group['items'] as $item) : ?>
|
||
<?php
|
||
$pid = $item->get_id();
|
||
$thumbnail = $item->get_image('woocommerce_thumbnail');
|
||
?>
|
||
<div class="yoone-bundle-item-card">
|
||
<div class="item-image">
|
||
<a href="<?php echo esc_url(get_permalink($pid)); ?>" target="_blank">
|
||
<?php echo $thumbnail ? $thumbnail : wc_placeholder_img('woocommerce_thumbnail'); ?>
|
||
</a>
|
||
</div>
|
||
<h4 class="item-title">
|
||
<a href="<?php echo esc_url(get_permalink($pid)); ?>" target="_blank"><?php echo esc_html($item->get_name()); ?></a>
|
||
</h4>
|
||
<div class="item-price">
|
||
<?php echo wp_kses_post($item->get_price_html()); ?>
|
||
</div>
|
||
<div class="item-quantity">
|
||
<input type="number" min="0" step="1" class="yoone-bundle-qty" name="yoone_bundle_components[<?php echo esc_attr($pid); ?>]" value="0" placeholder="0" data-unit-price="<?php echo esc_attr($item->get_price()); ?>" />
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
<?php endif; ?>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
<?php endif; ?>
|
||
</form>
|
||
<?php do_action('woocommerce_after_add_to_cart_form'); ?>
|
||
</div>
|