Dynamic price calculation based on custom field in

2019-01-20 12:56发布

问题:

In Woocommerce on each product page there is a Text Box, which allows users to enter their own custom text. This text is then applied to the product, with the customer being charged on a per letter basis.

I have managed to get everything working, apart from the Maths Logic. When a visitor enters x amount of letters, the current Maths logic correctly calculates the Product Price + Cost of Custom Letters and outputs this sum to the Basket Widget.

Where I am running into problems, is when the visitor goes to the Basket Page, the Cost of the Custom Lettering is doubled.

I just cannot seem to work out why this is.

Create Text Field in Product Dashboard:

<?php

/*Create Text Field in Product Dashboard*/

function add_text_field_product_dashboard(){

   global $post;

   $input_checkbox = get_post_meta( $post->ID, '_custom_text_option', true );
   if( empty( $input_checkbox ) || $input_checkbox == 'no' ) $input_checkbox = '';

    echo '<div class="product_custom_field">';

    /*Product Checkbox Field*/
    woocommerce_wp_checkbox(
        array(
            'id'        => '_custom_text_option',
            'desc'      =>  __('set custom custom text field', 'woocommerce'),
            'label'     => __('Display custom custom text field', 'woocommerce'),
            'desc_tip'  => 'true',
            'value'     => $input_checkbox
        )
    );
    /*Minimum Letter Text Box*/
    woocommerce_wp_text_input(
        array(
            'id'        => '_minimum_custom_text_option',
            'name'      => '_minimum_custom_text_option',
            'desc'      =>  __('set custom minimum Lettering text field', 'woocommerce'),
            'label'     => __('Minimum Letters', 'woocommerce'),
            'desc_tip'  => 'true'
        )
    );
    /*Maximum Letter Text Box*/
    woocommerce_wp_text_input(
        array(
            'id'        => '_maximum_custom_text_option',
            'desc'      =>  __('set custom maximum Lettering text field', 'woocommerce'),
            'label'     => __('Maximum Letters', 'woocommerce'),
            'desc_tip'  => 'true'
        )
    );
    /*Cost Per Letter Pricing*/
    woocommerce_wp_text_input(
        array(
            'id'        => '_pricing_custom_text_option',
            'desc'      =>  __('set custom pricing Lettering text field', 'woocommerce'),
            'label'     => __('Cost Per Letter', 'woocommerce'),
            'desc_tip'  => 'true'
        )
    );

    echo '</div>';
}
add_action('woocommerce_product_options_advanced', 'add_text_field_product_dashboard');
?>

Save Text Field Entries:

<?php
/*Save Inputted Entries, in the Product Dashboard Text Fields.*/

/*Checkbox Field*/
 function woocommerce_product_custom_fields_save($post_id){               
    $_custom_text_option = isset( $_POST['_custom_text_option'] ) ? 'yes' : '';
    update_post_meta( $post_id, '_custom_text_option', $_custom_text_option );       
 }
add_action('woocommerce_process_product_meta', 'woocommerce_product_custom_fields_save');

/*Save Minimum Letters*/
function woocommerce_product_custom_fields_save1($post_id){
    if ( isset( $_POST['_minimum_custom_text_option'] ) )
        update_post_meta($post_id, '_minimum_custom_text_option', esc_attr( $_POST['_minimum_custom_text_option'] ));
}
add_action( 'woocommerce_process_product_meta', 'woocommerce_product_custom_fields_save1' );

/*Save Maximum Letters*/
function woocommerce_product_custom_fields_save2($post_id){
    if ( isset( $_POST['_maximum_custom_text_option'] ) )
        update_post_meta($post_id, '_maximum_custom_text_option', esc_attr( $_POST['_maximum_custom_text_option'] ));
}
add_action( 'woocommerce_process_product_meta', 'woocommerce_product_custom_fields_save2' );

/*Save Cost Per Letter*/
function woocommerce_product_custom_fields_save3($post_id){
    if ( isset( $_POST['_pricing_custom_text_option'] ) )
        update_post_meta($post_id, '_pricing_custom_text_option', esc_attr( $_POST['_pricing_custom_text_option'] ));
}
add_action( 'woocommerce_process_product_meta', 'woocommerce_product_custom_fields_save3' );
?>

Output Custom Text to Product Page:

<?php
/*Output Custom Text Field to Product Page*/

function add_custom_text_field() {
    global $post;

    // Get the checkbox value
    $custom_option = get_post_meta( $post->ID, '_custom_text_option', true );

    // If is single product page and have the "custom text option" enabled we display the field
    if ( is_product() && ! empty($custom_option) ) {
?>      
        <div> 
            <label class="product-custom-text-label" for="custom_text"><?php _e( 'Custom Letters:', 'woocommerce'); ?><br>
                <input style="min-width:220px" type="text" class="product-counter" name="custom_text" placeholder="<?php _e( 'Enter Your Custom Letters ...', 'woocommerce'); ?>" minlength="<?php global $post; echo get_post_meta($post->ID,'_minimum_custom_text_option',true);?>" maxlength="<?php global $post; echo get_post_meta($post->ID,'_maximum_custom_text_option',true);?>" />
            </label>
        </div><br>
<?php
    }
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_text_field', 0 );
?>
?>

Append to Cart:

<?php  
/*Append to Cart once Shoper adds to Cart*/

function save_custom_text( $cart_item_data, $product_id ) {
    if( isset( $_POST['custom_text'] ) && !empty( $_POST['custom_text'] ) ) {
        $cart_item_data[ "custom_text" ] = esc_attr( $_POST['custom_text'] );     
    }

    return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_text', 99, 2 );
?>

Sum of Product plus Cost Per Letter Entered:

<?php 
/*Sum of Product Price plus Custom Text*/

function calculate_custom_text_fee( $cart_object ) {  
    foreach ( $cart_object->get_cart() as $cart_item ) {
        // Checking that we got the custom text in cart object
        if( ! empty( $cart_item["custom_text"] ) ) {

            // Quantity of characters entered into Custom Text Field:
            $lenght = strlen( $cart_item["custom_text"] );

            // get the custom pricing for this product
            $pricing_custom = get_post_meta( $cart_item['product_id'], '_pricing_custom_text_option', true );

            // Characters Entered Multiplied by Cost of Each Letter:
            $custom_text_fee = $lenght * $pricing_custom; 

            // get product price
            $price = floatval( $cart_item['data']->get_price() );

            // set new price
            $cart_item['data']->set_price( $price + $custom_text_fee );
        }
    }
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_custom_text_fee', 99, 1 );
?>

Output to Cart Description:

<?php 
/*Output to Cart Description*/

function render_meta_on_cart_and_checkout( $cart_data, $cart_item = null ) {
    $meta_items = array();


    if( !empty( $cart_data ) ) {
        $meta_items = $cart_data;
    }

    if( isset( $cart_item["custom_text"] ) ) {
        $meta_items[] = array( "name" => "Your Custom Text", "value" => $cart_item["custom_text"] );
    }

    return $meta_items;
}
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 99, 2 );
?>

Ensure Product is in Emails:

<?php 
/*Ensure Product is in Email Notifications*/
function custom_text_order_meta_handler( $item_id, $values, $cart_item_key ) {

    if( isset( $values["custom_text"] ) ) {
        wc_add_order_item_meta( $item_id, "Custom Text", $values["custom_text"] );
    }   
}
add_action( 'woocommerce_add_order_item_meta', 'custom_text_order_meta_handler', 99, 3 );
?>

Is anyone able to see where I could be going wrong, which is causing my Maths to double the costs in the Basket page?

Just to be clear, the Product Price is called fine. That is not doubled. Just the cost of the Custom Text. I feel there may be an issue with $price but I am just not so sure.

Any pointers, on this matter, would be greatly appreciated.

回答1:

UPDATE

To avoid this problem, I make before the new price calculation and save it in cart object using woocommerce_add_cart_item_data hook (the same hook used to save the custom text in cart object).

Note: For your text I am using trim() php function to remove white spaces from the length letter count… If you dont need it you can remove it.

I have revisited your code making some changes:

// Add custom fields in "product data" settings metabox ("Advanced" tab)
add_action('woocommerce_product_options_advanced', 'add_text_field_product_dashboard');
function add_text_field_product_dashboard(){

   global $post;

    echo '<div class="product_custom_field">';

    // Checkbox Field
    woocommerce_wp_checkbox( array(
        'id'        => '_custom_text_option',
        'description'      =>  __('set custom custom text field', 'woocommerce'),
        'label'     => __('Display custom custom text field', 'woocommerce'),
        'desc_tip'  => 'true',
    ) );

    // Minimum Letter Text Box
    woocommerce_wp_text_input( array(
        'id'        => '_minimum_custom_text_option',
        'label'     => __('Minimum Letters', 'woocommerce'),
        'description' =>  __('set custom minimum Lettering text field', 'woocommerce'),
        'desc_tip'  => 'true',
    ) );

    // Maximum Letter Text Box
    woocommerce_wp_text_input( array(
        'id'        => '_maximum_custom_text_option',
        'label'     => __('Maximum Letters', 'woocommerce'),
        'description' => __('set custom maximum Lettering text field', 'woocommerce'),
        'desc_tip'  => 'true'
    ) );

    // Cost Per Letter Pricing
    woocommerce_wp_text_input( array(
        'id'        => '_pricing_custom_text_option',
        'label'     => __('Cost Per Letter', 'woocommerce'),
        'description' => __('set custom pricing Lettering text field', 'woocommerce'),
        'desc_tip'  => 'true'
    ) );

    echo '</div>';
}

// Save Inputted Entries, in the Product Dashboard Text Fields.
add_action('woocommerce_process_product_meta', 'woocommerce_product_custom_fields_save');
 function woocommerce_product_custom_fields_save($post_id){
    // Checkbox Field
    $checkbox = isset( $_POST['_custom_text_option'] ) ? 'yes' : 'no';
    update_post_meta( $post_id, '_custom_text_option', $checkbox );

    // Save Minimum Letters
    if ( isset( $_POST['_minimum_custom_text_option'] ) )
        update_post_meta($post_id, '_minimum_custom_text_option', sanitize_text_field( $_POST['_minimum_custom_text_option'] ) );

    // Save Maximum Letters
    if ( isset( $_POST['_maximum_custom_text_option'] ) )
        update_post_meta($post_id, '_maximum_custom_text_option', sanitize_text_field( $_POST['_maximum_custom_text_option'] ) );

    // Save Cost Per Letter
    if ( isset( $_POST['_pricing_custom_text_option'] ) )
        update_post_meta($post_id, '_pricing_custom_text_option', sanitize_text_field( $_POST['_pricing_custom_text_option'] ) );
}


// Output Custom Text Field to Product Page
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_text_field', 0 );
function add_custom_text_field() {
    global $post;

    // Get the checkbox value
    $custom_option = get_post_meta( $post->ID, '_custom_text_option', true );

    // If is single product page and have the "custom text option" enabled we display the field
    if ( is_product() && ! empty($custom_option) ) {
?>
        <div>
            <label class="product-custom-text-label" for="custom_text"><?php _e( 'Custom Letters:', 'woocommerce'); ?><br>
                <input style="min-width:220px" type="text" class="product-counter" name="custom_text" placeholder="<?php _e( 'Enter Your Custom Letters ...', 'woocommerce'); ?>" minlength="<?php global $post; echo get_post_meta($post->ID,'_minimum_custom_text_option',true);?>" maxlength="<?php global $post; echo get_post_meta($post->ID,'_maximum_custom_text_option',true);?>" />
            </label>
        </div><br>
<?php
    }
}

// Set custom text and  calculated price as custom cart data in the cart item
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_data_in_cart_object', 30, 3 );
function save_custom_data_in_cart_object( $cart_item_data, $product_id, $variation_id ) {
    if( ! isset( $_POST['custom_text'] ) || empty( $_POST['custom_text'] ) )
        return $cart_item_data;

    // Get the custom text cost by letter
    $pricing_custom = (float) get_post_meta( $product_id, '_pricing_custom_text_option', true );

    // Get an instance of the WC_Product object
    $product = $variation_id > 0 ? wc_get_product($variation_id) : wc_get_product($product_id);
    $product_price = (float) $product->get_price(); // Get the product price

    // Get the text
    $custom_text = sanitize_text_field ( $_POST['custom_text'] );
    // Get lenght (trimming white spaces)
    $lenght = (float) strlen ( trim( $custom_text ) );

    // Set the text and the calculated price as custom cart data in the cart item
    $cart_item_data['custom_data']['price'] = $product_price + ( $lenght * $pricing_custom );
    $cart_item_data['custom_data']['text']  = $custom_text;

    return $cart_item_data;
}

// Display Custom text in cart and checkout pages
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 99, 2 );
function render_meta_on_cart_and_checkout( $cart_data, $cart_item = null ) {

    if( isset( $cart_item['custom_data']['text'] ) )
        $cart_data[] = array( "name" => "Your Custom Text", "value" => $cart_item["custom_data"]["text"] );

    return $cart_data;
}

// Set the new calculated price of the cart item
add_action( 'woocommerce_before_calculate_totals', 'calculate_custom_text_fee', 99, 1 );
function calculate_custom_text_fee( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;

    foreach ( $cart->get_cart() as $cart_item ) {
        if( isset( $cart_item['custom_data']['price'] ) ) {
            // Get the new calculated price
            $new_price = (float) $cart_item['custom_data']['price'];

            // Set the new calculated price
            $cart_item['data']->set_price( $new_price );
        }
    }
}

// Save the custom text as order item data (displaying it in order and notifications)
add_action( 'woocommerce_add_order_item_meta', 'custom_text_order_meta_handler', 99, 3 );
function custom_text_order_meta_handler( $item_id, $values, $cart_item_key ) {

    if( isset( $values['custom_data']['text'] ) )
        wc_add_order_item_meta( $item_id, "Custom Text", $values["custom_data"]["text"] );
}

Code goes in function.php file of your active child theme (or active theme).

Tested and works



回答2:

To get the product price by id:

$_product = wc_get_product( $product_id );

$_product->get_regular_price();

$_product->get_sale_price();

$_product->get_price();

OR, Get regular price :

$price = get_post_meta( get_the_ID(), '_regular_price', true); // $price will return regular price

Get sale price:

$sale = get_post_meta( get_the_ID(), '_sale_price', true); // $sale will return sale price