Add custom gift option

User
add option to show custom gift item in cart page and add it via click on checkbox to cart table all using ajax woocommerce
Assistant
To **add a custom gift item to the cart via a checkbox on the WooCommerce cart page using AJAX**, follow this step-by-step guide.

### 🧩 Scenario:
You want to show a checkbox:
☑️ “Add a Free Gift (Custom Product)”
If checked, a **custom product (e.g., gift)** is added to the cart dynamically **without page reload**, using AJAX.

## ✅ Step-by-Step Implementation

### 1. **Define the Free Gift Product ID**

Add this to `functions.php` or a custom plugin:

function get_custom_gift_product_id() {
    return 1234; // Replace with actual Product ID of the free gift
}

### 2. **Show the Gift Checkbox on Cart Page**

Add this to `functions.php`:

add_action('woocommerce_cart_coupon', 'add_free_gift_checkbox_on_cart');

function add_free_gift_checkbox_on_cart() {
    $gift_product_id = get_custom_gift_product_id();
    $is_in_cart = false;

    foreach (WC()->cart->get_cart() as $cart_item) {
        if ($cart_item['product_id'] == $gift_product_id) {
            $is_in_cart = true;
            break;
        }
    }

    ?>
    <div style="margin-top:20px;" id="custom-gift-checkbox">
        <label>
            <input type="checkbox" id="add_free_gift" <?php checked($is_in_cart); ?> />
            Add a Free Gift to Your Cart
        </label>
        <span class="ajax-loader" style="display:none;">Adding...</span>
    </div>
    <script type="text/javascript">
    jQuery(function($) {
        $('#add_free_gift').on('change', function() {
            $('.ajax-loader').show();

            $.ajax({
                url: '<?php echo admin_url('admin-ajax.php'); ?>',
                method: 'POST',
                data: {
                    action: 'toggle_free_gift',
                    checked: $(this).is(':checked') ? 1 : 0
                },
                success: function(response) {
                    location.reload(); // Refresh cart to update totals
                }
            });
        });
    });
    </script>
    <?php
}

### 3. **Handle the AJAX Request**

Add to `functions.php`:

add_action('wp_ajax_toggle_free_gift', 'ajax_add_remove_free_gift');
add_action('wp_ajax_nopriv_toggle_free_gift', 'ajax_add_remove_free_gift');

function ajax_add_remove_free_gift() {
    $gift_product_id = get_custom_gift_product_id();

    if (isset($_POST['checked']) && $_POST['checked']) {
        // Add gift if not already in cart
        $exists = false;
        foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
            if ($cart_item['product_id'] == $gift_product_id) {
                $exists = true;
                break;
            }
        }
        if (!$exists) {
            WC()->cart->add_to_cart($gift_product_id, 1);
        }
    } else {
        // Remove gift if exists
        foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
            if ($cart_item['product_id'] == $gift_product_id) {
                WC()->cart->remove_cart_item($cart_item_key);
                break;
            }
        }
    }

    wp_send_json_success();
}

### ✅ Result

– Users see a checkbox to add a gift item.
– On checking it, the item is added to cart via AJAX.
– Unchecking removes it.
– Page refreshes automatically to update the cart.

### 🛠️ Optional Improvements

– Add a loading spinner instead of text `Adding…`
– Prevent manually removing the gift item (by hiding its remove link)
– Style the checkbox with custom CSS

Would you like any of these enhancements too?

User
but we don't want referejs the cart page to view updated gift cart item to cart
Assistant
Great! To **add/remove the gift item via AJAX without refreshing the cart page**, and also **update the cart totals and table in real-time**, follow this improved setup using AJAX and WooCommerce fragments.

## ✅ Goal
– Checkbox to add/remove a gift item
– Gift item gets added/removed via AJAX
– Cart totals and cart table update **without page reload**

## 🔧 Full Implementation

### 1. **Add Checkbox to Cart Page**

In `functions.php`:

add_action('woocommerce_cart_coupon', 'add_free_gift_checkbox_on_cart');

function add_free_gift_checkbox_on_cart() {
    $gift_product_id = get_custom_gift_product_id();
    $is_in_cart = false;

    foreach (WC()->cart->get_cart() as $cart_item) {
        if ($cart_item['product_id'] == $gift_product_id) {
            $is_in_cart = true;
            break;
        }
    }
    ?>
    <div id="custom-gift-checkbox" style="margin-top: 20px;">
        <label>
            <input type="checkbox" id="add_free_gift" <?php checked($is_in_cart); ?> />
            Add a Free Gift to Your Cart
        </label>
        <span class="ajax-loader" style="display:none;">Processing...</span>
    </div>
    <script type="text/javascript">
    jQuery(function($) {
        $('#add_free_gift').on('change', function() {
            $('.ajax-loader').show();

            $.ajax({
                url: wc_cart_params.ajax_url,
                type: 'POST',
                data: {
                    action: 'toggle_free_gift',
                    checked: $(this).is(':checked') ? 1 : 0,
                },
                success: function(response) {
                    if (response.success) {
                        $(document.body).trigger('update_checkout'); // for checkout page if needed
                        $(document.body).trigger('wc_fragment_refresh'); // refresh cart fragments
                    }
                    $('.ajax-loader').hide();
                }
            });
        });
    });
    </script>
    <?php
}

### 2. **Add the Gift Product (AJAX Logic)**

Also in `functions.php`:

function get_custom_gift_product_id() {
    return 1234; // Change this to your actual gift product ID
}

add_action('wp_ajax_toggle_free_gift', 'ajax_add_remove_free_gift');
add_action('wp_ajax_nopriv_toggle_free_gift', 'ajax_add_remove_free_gift');

function ajax_add_remove_free_gift() {
    $gift_product_id = get_custom_gift_product_id();

    if (isset($_POST['checked']) && $_POST['checked']) {
        // Add to cart if not present
        $exists = false;
        foreach (WC()->cart->get_cart() as $cart_item) {
            if ($cart_item['product_id'] == $gift_product_id) {
                $exists = true;
                break;
            }
        }

        if (!$exists) {
            WC()->cart->add_to_cart($gift_product_id, 1);
        }
    } else {
        // Remove from cart
        foreach (WC()->cart->get_cart() as $cart_item_key => $cart_item) {
            if ($cart_item['product_id'] == $gift_product_id) {
                WC()->cart->remove_cart_item($cart_item_key);
                break;
            }
        }
    }

    WC_AJAX::get_refreshed_fragments(); // Respond with updated cart fragments
    wp_die();
}

### 3. **Make Sure `wc_cart_fragments` Is Enqueued**

WooCommerce loads it automatically, but double check with:

add_action('wp_enqueue_scripts', function () {
    if (is_cart()) {
        wp_enqueue_script('wc-cart-fragments');
    }
});

### 4. ✅ Result

– ✅ No page reload
– ✅ Gift item appears/disappears dynamically
– ✅ Cart totals and table update instantly
– ✅ Works on Cart and Checkout pages

### 🔄 Bonus: Hide “Remove” Link on Gift (Optional)

add_filter('woocommerce_cart_item_remove_link', function($link, $cart_item_key) {
    $gift_id = get_custom_gift_product_id();
    $cart = WC()->cart->get_cart();

    if (isset($cart[$cart_item_key]['product_id']) && $cart[$cart_item_key]['product_id'] == $gift_id) {
        return ''; // Don't show remove link for gift
    }

    return $link;
}, 10, 2);

Would you like to make the gift **free only when cart total is above certain amount**, or **limit it to once per user**?

User
public function pfc_gift_add_to_cart() {

// if( isset( $_POST['action_type'] ) && $_POST['action_type'] == 'add' ) {

// $this->pfc_remove_last_gift();
// }
if( isset( $_POST['action_type'] ) && $_POST['action_type'] == 'add' ) {

if( is_numeric( $_POST['prd_id'] ) ) {

if( isset( $_POST['type'] ) && $_POST['type'] ) {
$this->remove_cart_products_by_type( $_POST['type'] );
}
WC()->cart->add_to_cart( $_POST['prd_id'] );
}
update_user_meta( get_current_user_id(), 'pfc_last_added_gift', $_POST['prd_id'] );
}
elseif( isset( $_POST['action_type'] ) && $_POST['action_type'] == 'remove' && is_numeric( $_POST['prd_id'] ) ) {

$product_id = $_POST['prd_id'];
$product_cart_id = WC()->cart->generate_cart_id( $product_id );
$cart_item_key = WC()->cart->find_product_in_cart( $product_cart_id );
if( $cart_item_key ) {
WC()->cart->remove_cart_item( $cart_item_key );
}
}
// apply_filters( 'woocommerce_update_cart_action_cart_updated', true );

// Calculate totals and get the cart fragments
WC()->cart->calculate_totals();

// Get the updated fragments
$fragments = [];

ob_start();
?>

Assistant
To improve your existing method `pfc_gift_add_to_cart()` and **properly return cart fragments** (so totals and table update without page reload), you'll need to:

1. Add the gift product to the cart or remove it via AJAX.
2. Properly **trigger WooCommerce’s fragment updates** using `WC_AJAX::get_refreshed_fragments()`.

Here’s your **updated and cleaned-up function**:

public function pfc_gift_add_to_cart() {
    $product_id = isset($_POST['prd_id']) ? intval($_POST['prd_id']) : 0;
    $action_type = sanitize_text_field($_POST['action_type'] ?? '');
    $type = sanitize_text_field($_POST['type'] ?? '');

    if ($action_type === 'add' && $product_id) {

        if (!empty($type)) {
            $this->remove_cart_products_by_type($type); // custom method
        }

        WC()->cart->add_to_cart($product_id);

        update_user_meta(get_current_user_id(), 'pfc_last_added_gift', $product_id);

    } elseif ($action_type === 'remove' && $product_id) {

        $product_cart_id = WC()->cart->generate_cart_id($product_id);
        $cart_item_key = WC()->cart->find_product_in_cart($product_cart_id);

        if ($cart_item_key) {
            WC()->cart->remove_cart_item($cart_item_key);
        }
    }

    // Recalculate totals
    WC()->cart->calculate_totals();

    // Output WooCommerce cart fragments (table, totals, mini cart etc.)
    WC_AJAX::get_refreshed_fragments();

    wp_die(); // Always call wp_die() in AJAX
}

## ✅ What This Does:
– Handles both `add` and `remove` actions.
– Supports removing existing gifts of the same type (if passed).
– Returns **all live cart fragments**, including:
– Updated mini cart
– Updated totals
– Updated cart table
– Avoids manual `echo json_encode`, which is fragile and incomplete.

## 📝 JS Example to Handle It:

Here’s a sample jQuery AJAX call that works with the above:

jQuery(document).ready(function ($) {
    $('#gift-checkbox').on('change', function () {
        let isChecked = $(this).is(':checked');
        let productId = $(this).data('product-id');
        let type = $(this).data('gift-type'); // optional

        $.ajax({
            url: wc_cart_params.ajax_url,
            type: 'POST',
            data: {
                action: 'pfc_gift_add_to_cart',
                action_type: isChecked ? 'add' : 'remove',
                prd_id: productId,
                type: type
            },
            success: function (response) {
                // WooCommerce fragments are auto refreshed by WC_AJAX::get_refreshed_fragments
                console.log('Gift item updated.');
            }
        });
    });
});

Would you also like to:
– Prevent adding the same gift twice?
– Limit to one gift per user?
– Add conditional logic (e.g. cart total > X)?

User
not working newly added cotnent not showing without reload cart apge
Assistant
Thanks — since you're calling `WC_AJAX::get_refreshed_fragments()` correctly but **the cart table isn’t updating**, it’s because **`[woocommerce_cart]` does not update dynamically via fragments**.

### 🔍 Root Cause

The `[woocommerce_cart]` shortcode **renders the full cart page only during the initial page load**.
Even if you return it via AJAX (like in your earlier `echo json_encode(['cart' => do_shortcode(…)]);`), it won’t **magically replace the live cart table** unless you also **replace that HTML in the DOM manually**.

## ✅ ✅ Working Solution

Here’s how to **dynamically update the cart page content (table + totals)** when you add/remove the gift via AJAX:

### ✅ 1. Keep Your PHP AJAX Handler Like This:

Keep this version (no need to return the shortcode):

public function pfc_gift_add_to_cart() {
    $product_id = isset($_POST['prd_id']) ? intval($_POST['prd_id']) : 0;
    $action_type = sanitize_text_field($_POST['action_type'] ?? '');
    $type = sanitize_text_field($_POST['type'] ?? '');

    if ($action_type === 'add' && $product_id) {
        if (!empty($type)) {
            $this->remove_cart_products_by_type($type);
        }
        WC()->cart->add_to_cart($product_id);
        update_user_meta(get_current_user_id(), 'pfc_last_added_gift', $product_id);
    } elseif ($action_type === 'remove' && $product_id) {
        $product_cart_id = WC()->cart->generate_cart_id($product_id);
        $cart_item_key = WC()->cart->find_product_in_cart($product_cart_id);
        if ($cart_item_key) {
            WC()->cart->remove_cart_item($cart_item_key);
        }
    }

    WC()->cart->calculate_totals();
    WC_AJAX::get_refreshed_fragments(); // this includes mini-cart, totals, etc.

    wp_die();
}

### ✅ 2. Update JS to Refresh Full Cart Table via AJAX

You must manually reload the cart table **using another AJAX call to fetch `[woocommerce_cart]`**, and replace the table HTML.

Here’s the updated **JavaScript**:

jQuery(document).ready(function ($) {
    $('#gift-checkbox').on('change', function () {
        let isChecked = $(this).is(':checked');
        let productId = $(this).data('product-id');
        let giftType = $(this).data('gift-type') || '';

        $.ajax({
            url: wc_cart_params.ajax_url,
            type: 'POST',
            data: {
                action: 'pfc_gift_add_to_cart',
                action_type: isChecked ? 'add' : 'remove',
                prd_id: productId,
                type: giftType
            },
            success: function () {
                // Trigger WooCommerce fragment refresh (totals, mini cart)
                $(document.body).trigger('wc_fragment_refresh');

                // 👇 Replace full cart content manually
                $.ajax({
                    url: window.location.href, // cart page
                    type: 'GET',
                    success: function (data) {
                        const newCartTable = $(data).find('.woocommerce-cart-form'); // replace cart table
                        const newTotals = $(data).find('.cart_totals');

                        $('.woocommerce-cart-form').replaceWith(newCartTable);
                        $('.cart_totals').replaceWith(newTotals);
                    }
                });
            }
        });
    });
});

### ✅ What This Fixes

– Keeps your cart page in place without full reload
– Dynamically replaces cart table and totals
– Works with **any cart item changes via AJAX**
– Uses `WC_AJAX::get_refreshed_fragments()` for totals and mini cart

Let me know if your theme uses a custom cart layout (e.g. Avada, Divi, Elementor), and I’ll adjust selectors accordingly.

User
i want to udate cart table at cart page
Assistant
Perfect — your goal is **to update the full cart table on the cart page via AJAX** after adding/removing a custom gift product **without reloading the page**.

### ✅ You need two things:

1. Your PHP AJAX must **handle cart add/remove**, which you're already doing ✅
2. Your JS must:
– Trigger that AJAX call ✅
– **Then fetch the updated cart table HTML** from the cart page
– **Replace** the `.woocommerce-cart-form` element dynamically

## ✅ Final Working Solution

### 🔧 Step 1: Use this **PHP Handler** (you already have it, just make sure it's like this)

public function pfc_gift_add_to_cart() {
    $product_id = isset($_POST['prd_id']) ? intval($_POST['prd_id']) : 0;
    $action_type = sanitize_text_field($_POST['action_type'] ?? '');
    $type = sanitize_text_field($_POST['type'] ?? '');

    if ($action_type === 'add' && $product_id) {
        if (!empty($type)) {
            $this->remove_cart_products_by_type($type);
        }
        WC()->cart->add_to_cart($product_id);
        update_user_meta(get_current_user_id(), 'pfc_last_added_gift', $product_id);
    } elseif ($action_type === 'remove' && $product_id) {
        $product_cart_id = WC()->cart->generate_cart_id($product_id);
        $cart_item_key = WC()->cart->find_product_in_cart($product_cart_id);
        if ($cart_item_key) {
            WC()->cart->remove_cart_item($cart_item_key);
        }
    }

    WC()->cart->calculate_totals();

    // Return success (we’ll reload cart table from frontend)
    wp_send_json_success();
}

✅ No need to return fragments or shortcode here — we'll handle that on frontend.

### 🧠 Step 2: JavaScript to update cart table & totals

Add this in your JS file (or inline, e.g., in `woocommerce_cart_coupon` hook):

jQuery(function ($) {
    $('#gift-checkbox').on('change', function () {
        let isChecked = $(this).is(':checked');
        let productId = $(this).data('product-id');
        let giftType = $(this).data('gift-type') || '';

        $.ajax({
            url: wc_cart_params.ajax_url,
            type: 'POST',
            data: {
                action: 'pfc_gift_add_to_cart',
                action_type: isChecked ? 'add' : 'remove',
                prd_id: productId,
                type: giftType
            },
            success: function (response) {
                if (response.success) {
                    // 🔁 Fetch updated cart page HTML (we need only table)
                    $.get(window.location.href, function (data) {
                        const updatedCartTable = $(data).find('.woocommerce-cart-form');
                        const updatedCartTotals = $(data).find('.cart_totals');

                        $('.woocommerce-cart-form').replaceWith(updatedCartTable);
                        $('.cart_totals').replaceWith(updatedCartTotals);
                    });
                }
            }
        });
    });
});

### ✅ Example HTML Checkbox in Cart Page

Add this via `woocommerce_cart_coupon` or manually in template:

<input type="checkbox" id="gift-checkbox"
       data-product-id="1234"
       data-gift-type="gift_type"
       <?php checked( WC()->cart->find_product_in_cart( WC()->cart->generate_cart_id(1234) ) ); ?> />
<label for="gift-checkbox">Add a Free Gift</label>

Replace `1234` with your gift product ID.

## ✅ Result

– Add/remove gift via checkbox
– AJAX hits your handler
– JS then reloads `.woocommerce-cart-form` and `.cart_totals` from the current cart page
– No full page reload needed 🚀

Let me know if you're using a **custom WooCommerce theme** or **Ajax-based cart page** — I’ll adjust selectors and compatibility for that.

User
update this js code
$.post(global_object.ajax_url, data, function (resp) {

// if( $( 'button[name="update_cart"]' ).length > 0 && '' ) {

// $( 'button[name="update_cart"]' ).removeAttr( 'disabled' );
// $( 'button[name="update_cart"]' ).trigger( 'click' );
// }
if ($is_cart && resp.cart && $('.content-area.page-wrapper .woocommerce')) {

resp.cart = resp.cart.replace(/pfc-checks/g, '');

if ($('.pfc-temp-cart').length

Assistant
Here is your **updated and corrected JS code**, rewritten cleanly and clearly to:

– Handle replacement of cart contents properly
– Use `.html()` correctly (it needs `.html(content.html())` not `.html(content)`)
– Ensure compatibility with WooCommerce cart page
– Remove unnecessary commented blocks
– Add error safety and smoother handling

### ✅ Updated JS (clean + functional)

$.post(global_object.ajax_url, data, function (resp) {

    if ($is_cart && resp.cart && $('.content-area.page-wrapper .woocommerce').length) {

        // Clean unwanted classes
        resp.cart = resp.cart.replace(/pfc-checks/g, '');

        // Create a hidden div to hold the refreshed cart HTML
        if ($('.pfc-temp-cart').length === 0) {
            $('body').append('<div class="pfc-temp-cart" style="display: none">' + resp.cart + '</div>');
        } else {
            $('.pfc-temp-cart').html(resp.cart);
        }

        // Extract the new cart table and totals
        const updatedCartTable = $('.pfc-temp-cart').find('.woocommerce-cart-form__contents');
        const updatedCartTotals = $('.pfc-temp-cart').find('.cart_totals');

        // Replace the current cart table and totals
        if (updatedCartTable.length) {
            $('.woocommerce-cart-form__contents').html(updatedCartTable.html());
        }

        if (updatedCartTotals.length) {
            $('.cart_totals').html(updatedCartTotals.html());
        }

        // Hide custom loader if used
        $('.pfc-custom-load').hide();
    }

    // Refresh WooCommerce fragments (like mini-cart, if any)
    $(document.body).trigger("wc_fragment_refresh");
    $(document.body).trigger("update_checkout");

}, 'json');

### ✅ Optional Improvements
– If you're also updating cart notices (e.g. success messages), extract `.woocommerce-notices-wrapper` from `.pfc-temp-cart` and inject it too.
– To support Divi or Elementor themes, you may want to use broader selectors.
– Wrap the whole thing in `$(document).ready()` or in your checkbox trigger callback.

Let me know if you want this wrapped into a reusable function or to also work for checkout/cart preview widgets.

Leave a Comment

Your email address will not be published. Required fields are marked *