在我的 WooCommerce 商店中,我有 15 种产品,每种产品应具有超过 2K 的变体(WooCommerce 允许创建最多 50 个变体),因此我使用了产品额外选项插件并创建了一些输入来检索客户输入。
价格将基于费率范围,例如:
这是产品输入字段 HTML:
<table class="thwepo-extra-options thwepo_simple" cellspacing="0">
<tbody>
<tr class="">
<td class="label leftside">
<label class="label-tag">Door height (mm)</label>
<abbr class="required" title="Required">*</abbr>
</td>
<td class="value leftside">
<input type="text" id="height884" name="height" placeholder="Height" value="" class="thwepof-input-field validate-required">
</td>
</tr>
<tr class="">
<td class="label leftside">
<label class="label-tag">Door width (mm)</label>
<abbr class="required" title="Required">*</abbr>
</td>
<td class="value leftside">
<input type="text" id="width784" name="width" placeholder="Width" value="" class="thwepof-input-field validate-required" maxlength="1800">
</td>
</tr>
<tr class="">
<td class="label leftside">
<label class="label-tag">Thickness (mm)</label>
<abbr class="required" title="Required">*</abbr>
</td>
<td class="value leftside">
<input type="text" id="thickness334" name="thickness" placeholder="Thickness" value="" class="thwepof-input-field validate-required">
</td>
</tr>
<tr class="">
<td class="label leftside">
<label class="label-tag">Material Type</label>
<abbr class="required" title="Required">*</abbr>
</td>
<td class="value leftside">
<select id="type_de_bois772" name="type_de_bois" placeholder="Agglo" value="Agglo" class="thwepof-input-field validate-required">
<option value="Agglo">Chipboard</option>
<option value="MDF">Medium (MDF)</option>
</select>
</td>
</tr>
</tbody>
</table>
这是我的 PHP 代码:
add_action('woocommerce_before_calculate_totals', 'update_product_price_based_on_weight');
function update_product_price_based_on_weight($cart) {
foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
// Get product object
$product = $cart_item['data'];
// Check if product is the one we want to modify
if ($product->get_name() === 'SERVO-DRIVE for AVENTOS HF') {
// Calculate weight based on inputs
$height = $_POST['height']; // Assuming the form submits this data
$width = $_POST['width'];
$thickness = $_POST['thickness'];
$material_type = $_POST['type_de_bois'];
// Calculate density based on material type
$density = ($material_type === 'MDF') ? 760 : 680;
// Calculate weight
$weight = ($height * $width * $thickness * $density) / 1000000000;
$force = $weight * $height;
// Adjust price based on weight
if ($force >= 2600 && $force <= 5349) {
$product->set_price(1000); // Adjust price for CASE 1
} elseif ($force >= 5350 && $force <= 8999) {
$product->set_price(2000); // Adjust price for CASE 2
} elseif ($force >= 9000 && $force <= 17250) {
$product->set_price(3000); // Adjust price for CASE 3
}
}
}
但我无法让它工作,无法设置正确的计算价格。我做错了什么?
在下面的代码中,根据您最后的评论,我使用 JavaScript 直接在产品页面上动态计算价格(基于您的计算算法)。您可能需要微调您的计算算法,因为当前的计算似乎不准确。
注意: 这不适用于购物车和结帐块,它适用于经典的 WooCommerce 购物车和结帐(基于模板)。
首先,在管理员编辑产品页面中,我们添加一个复选框以启用产品的动态价格计算:
// Admin product edit page (Check box to enable dynamic price)
add_action( 'woocommerce_product_options_pricing', 'enabling_product_dynamic_price_calculation' );
function enabling_product_dynamic_price_calculation() {
global $post, $product_object;
woocommerce_wp_checkbox( array(
'id' => 'dynamic_price',
'label' => __( 'Dynamic price', 'woocommerce' ),
'value' => $product_object->get_meta('dynamic_price') ? 'yes' : 'no',
'description' => __( 'Enable dynamic calculated price based on user input fields.', 'woocommerce' ),
) );
}
// Admin product: Save dynamic price setting option
add_action( 'woocommerce_admin_process_product_object', 'save_product_dynamic_price_setting_option' );
function save_product_dynamic_price_setting_option( $product ) {
$product->update_meta_data( 'dynamic_price', isset($_POST['dynamic_price']) && $_POST['dynamic_price'] === 'yes' ? '1' : '' );
}
您将得到:
您需要在正常价格中设置该产品的起始(最低)价格金额。
然后在商店和存档页面上,对于该产品,我们将添加到购物车按钮更改为产品页面的链接按钮。此外,我们还更改了价格显示,在常规价格中添加了“起始于”前缀:
// Replace loop add to cart button link with a link to the product
add_filter( 'woocommerce_loop_add_to_cart_link', 'replace_product_loop_add_to_cart_link', 100, 3 );
function replace_product_loop_add_to_cart_link( $button, $product, $args ) {
// Only for external products
if ( $product->get_meta('dynamic_price') ) {
$button = sprintf( '<a href="%s" class="%s" %s>%s</a>',
esc_url( $product->get_permalink() ),
esc_attr( 'button' ),
isset( $args['attributes'] ) ? wc_implode_html_attributes( $args['attributes'] ) : '',
esc_html__( 'Select options', 'woocommerce' )
);
}
return $button;
}
// Prefixing product price
add_filter( 'woocommerce_get_price_html', 'filter_get_price_html_callback', 10, 2 );
function filter_get_price_html_callback( $price_html, $product ){
if( $product->get_meta('dynamic_price') ) {
$price_html = sprintf('Starting from %s', $price_html);
}
return $price_html;
}
您将得到:
填充所有输入字段后,将计算并显示价格(替换“起始于”默认起始价格)。
// Utility function (your custom input fields settings)
function get_custom_fields_data( $options = false ) {
if ( $options ) {
return array(
'' => __('Select an option'),
'680' => __('Chipboard'),
'760' => __('Medium (MDF)'),
);
}
return array(
'height' => array( 'field' => 'height', 'type' => 'text', 'label' => __('Door height (mm)'), 'name' => __('Height'), ),
'width' => array( 'field' => 'width', 'type' => 'text', 'label' => __('Door width (mm)'), 'name' => __('width'), ),
'thickness' => array( 'field' => 'thickness', 'type' => 'text', 'label' => __('Thickness (mm)'), 'name' => __('Thickness'), ),
'material' => array( 'field' => 'material', 'type' => 'select', 'label' => __('Material Type (mm)'), 'name' => __('Material'), ),
);
}
// Display custom input fields in single product pages
add_action('woocommerce_before_add_to_cart_button', 'action_before_add_to_cart_button', 20);
function action_before_add_to_cart_button() {
global $product;
if ( $product->get_meta('dynamic_price') ) {
echo '<div class="custom-fields">';
foreach ( get_custom_fields_data() as $key => $values ) {
$args = array(
'type' => $values['type'],
'label' => $values['label'],
'class' => array( 'form-row-wide') ,
'placeholder' => $values['type'] === 'text' ? $values['name'] : '', // Optional
'required' => true,
);
if ( $values['type'] === 'select' ) {
$args['options'] = get_custom_fields_data( true );
}
woocommerce_form_field( $key, $args );
}
echo '<input type="hidden" name="price" value="" />
' . product_price_calculation_js( $product ) . '
</div>';
}
}
// Custom function that calculates the product price
function product_price_calculation_js( $product ) {
$zero_price = str_replace('0.00', '<span class="price-amount"></span>', wc_price(0));
ob_start();
?>
<script>
jQuery(function($){
var height = 0, width = 0, thickness = 0, density = 0, price = 0;
var originalPrice = $('p.price').html(), zeroPrice = '<?php echo $zero_price; ?>';
// Function that calculates the price
function calculatePrice( height, width, thickness, density ) {
var price = 0;
if ( height > 0 && width > 0 && thickness > 0 && density > 0 ) {
const weight = (height * width * thickness * density) / 1000000000,
rate = weight * height;
if ( rate < 5350 ) {
price = 1000.00;
} else if (rate >= 5350 && rate < 9000) {
price = 2000.00;
} else if (rate >= 9000) {
price = 3000.00;
}
}
return price;
}
$('form.cart').on('change mouseleave', '.custom-fields input[type=text], .custom-fields select', function(){
const fieldValue = $(this).val(), fieldKey = $(this).prop('name');
if ( fieldValue > 0 ) {
if ( fieldKey === 'height' ) {
height = fieldValue;
} else if ( fieldKey === 'width' ) {
width = fieldValue;
} else if ( fieldKey === 'thickness' ) {
thickness = fieldValue;
} else if ( fieldKey === 'material' ) {
density = fieldValue;
}
price = calculatePrice( height, width, thickness, density );
if ( price > 0 ) {
$('.custom-fields input[name=price]').val(price);
$('p.price').html(zeroPrice);
$('p.price span.price-amount').html(parseFloat(price).toFixed(2).replace('.', ','));
}
} else {
price = 0;
$('p.price').html(originalPrice);
$('.custom-fields input[name=price]').val('');
}
});
});
</script>
<?php
return ob_get_clean();
}
// Validate required product input fields
add_filter( 'woocommerce_add_to_cart_validation', 'single_product_custom_fields_validation', 10, 2 );
function single_product_custom_fields_validation( $passed, $product_id ) {
$product = wc_get_product( $product_id );
if ( $product->get_meta('dynamic_price') ) {
foreach ( get_custom_fields_data() as $key => $values ) {
if ( isset($_POST[$key]) && empty($_POST[$key]) ) {
wc_add_notice( sprintf( __('%s is a required field.', 'woocommerce'), '<strong>'.$values['label'].'</strong>'), "error" );
$passed = false;
}
}
}
return $passed;
}
// Save custom fields as custom cart item data
add_filter('woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 20, 2 );
function add_custom_cart_item_data( $cart_item_data, $product_id ) {
foreach ( get_custom_fields_data() as $key => $values ) {
if ( isset($_POST[$key]) && ! empty($_POST[$key]) ) {
$cart_item_data['custom'][$key] = sanitize_text_field($_POST[$key]);
}
}
if ( isset($_POST['price']) && ! empty($_POST['price']) ) {
$cart_item_data['custom']['price'] = sanitize_text_field($_POST['price']);
}
return $cart_item_data;
}
// Display custom fields in Cart and Checkout
add_filter( 'woocommerce_get_item_data', 'display_custom_cart_item_data', 20, 2 );
function display_custom_cart_item_data( $cart_data, $cart_item ) {
$options = get_custom_fields_data(true);
foreach ( get_custom_fields_data() as $key => $values) {
if( isset($cart_item['custom'][$key]) ) {
$cart_data[] = array(
'key' => $values['name'],
'value' => $values['type'] === 'select' ? $options[$cart_item['custom'][$key]] : $cart_item['custom'][$key],
);
}
}
return $cart_data;
}
您将得到:
// Cart and mini cart displayed calculated price
add_filter( 'woocommerce_cart_item_price', 'display_cart_item_price_html', 20, 2 );
function display_cart_item_price_html( $price_html, $cart_item ) {
if( isset($item['custom']['price']) ) {
$args = array( 'price' => $cart_item['custom']['price'] );
if ( WC()->cart->display_prices_including_tax() ) {
$price = wc_get_price_including_tax( $cart_item['data'], $args );
} else {
$price = wc_get_price_excluding_tax( $cart_item['data'], $args );
}
return wc_price( $price );
}
return $price_html;
}
// Change and set cart item custom calculated price
add_action('woocommerce_before_calculate_totals', 'set_custom_cart_item_price');
function set_custom_cart_item_price( $cart ) {
if ( is_admin() && !defined('DOING_AJAX') )
return;
foreach ( $cart->get_cart() as $item_key => $item ) {
if( isset($item['custom']['price']) ) {
$item['data']->set_price($item['custom']['price']);
}
}
}
// Save and display custom fields (custom order item metadata)
add_action( 'woocommerce_checkout_create_order_line_item', 'save_order_item_custom_meta_data', 10, 4 );
function save_order_item_custom_meta_data( $item, $cart_item_key, $values, $order ) {
foreach ( get_custom_fields_data() as $key => $data ) {
$options = get_custom_fields_data(true);
if( isset($values['custom'][$key]) ) {
$meta_value = $data['type'] === 'select' ? $options[$values['custom'][$key]] : $values['custom'][$key];
$item->update_meta_data($key, $meta_value);
}
}
}
// Add readable "meta key" label name replacement
add_filter('woocommerce_order_item_display_meta_key', 'filter_wc_order_item_display_meta_key', 10, 3 );
function filter_wc_order_item_display_meta_key( $display_key, $meta, $item ) {
if( $item->get_type() === 'line_item' ) {
foreach ( get_custom_fields_data() as $key => $values ) {
if( $meta->key === $key ) {
$display_key = $values['name'];
}
}
}
return $display_key;
}
代码位于子主题的functions.php 文件中(或插件中)。已测试并有效。