I am overriding the AJAX function to add a product using WooCommerce so I can send attributes to the cart as well (which doesn't work out of the box).
I followed the instructions on Update WooCommerce Cart After Adding Variable Product Via AJAX - which does what I want - but it doesn't work completely.
Everything is good when I am logged out. But when I log into Wordpress the PHP override doesn't work/gets ignored. I get no errors and no product is added to the cart. I tried many different approaches, but nothing helps.
function ajax_add_to_cart_script() {
$vars = array( 'ajax_url' => admin_url( 'admin-ajax.php' ) );
wp_enqueue_script( 'wc-variation-add-to-cart', get_template_directory_uri() . '/js/jquery.add-to-cart.js', array( 'jquery' ), null, true);
wp_localize_script( 'wc-variation-add-to-cart', 'WC_VARIATION_ADD_TO_CART', $vars );
}
add_action( 'wp_enqueue_scripts', 'ajax_add_to_cart_script' );
add_action( 'wp_ajax_nopriv_woocommerce_add_variation_to_cart', 'so_27270880_add_variation_to_cart' );
function so_27270880_add_variation_to_cart() {
ob_start();
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $_POST['product_id'] ) );
$quantity = empty( $_POST['quantity'] ) ? 1 : wc_stock_amount( $_POST['quantity'] );
$variation_id = isset( $_POST['variation_id'] ) ? absint( $_POST['variation_id'] ) : '';
$variations = ! empty( $_POST['variation'] ) ? (array) $_POST['variation'] : '';
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
if ( $passed_validation && WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
do_action( 'woocommerce_ajax_added_to_cart', $product_id );
if ( get_option( 'woocommerce_cart_redirect_after_add' ) == 'yes' ) {
wc_add_to_cart_message( $product_id );
}
// Return fragments
WC_AJAX::get_refreshed_fragments();
} else {
// If there was an error adding to the cart, redirect to the product page to show any errors
$data = array(
'error' => true,
'product_url' => apply_filters( 'woocommerce_cart_redirect_after_error', get_permalink( $product_id ), $product_id )
);
wp_send_json( $data );
}
die();
}
I am making sure that add_action( 'wp_ajax_nopriv_woocommerce_add_variation_to_cart', 'so_27270880_add_variation_to_cart' ); is added before the snippet to make sure it gets fired, which works when I am logged out - but not when I am logged in.
Anyone got any clue why?
Ping #helgatheviking
An update, it seems to work if I add the following to my functions.php
add_action( 'wp_ajax_nopriv_woocommerce_add_variation_to_cart', 'so_27270880_add_variation_to_cart' );
add_action( 'wp_ajax_woocommerce_add_variation_to_cart', 'so_27270880_add_variation_to_cart' );
Meaning, that one is for logged in admins and one is for normal users. Not sure why I need both because every example I have seen with this kind of extension/override only mentions the wp_ajax_nopriv_$function
Hope this helps anyone who have the same problem as me. And if anyone could explain to me why this is needed, I would be happy to hear it.
Related
My simple question is that how I can make this code working
esc_url( WC()->cart->get_cart_url->get_remove_url( $cart_item_key ) ),
the above i have tried and the current code is below
esc_url( $woocommerce->cart->get_remove_url( $cart_item_key ) ),
so instead my current url : example.com/?removed_item=1
should be something like example.com/checkout/?removed_item=1
Thanks for any suggestion
Try the below code. code will goes in your active theme functions.php file.
add_filter( 'woocommerce_get_remove_url', 'change_item_remove_url_to_checkout', 10, 1 );
function change_item_remove_url_to_checkout( $remove_url ) {
$cart_page_url = wc_get_page_permalink( 'cart' );
$replacement_url = wc_get_page_permalink( 'checkout' ); // Shop page
$remove_url = str_replace( $cart_page_url, $replacement_url, $remove_url );
return $remove_url;
}
How can I redirect back to the /cart after I click the 'Undo' link when I delete an item from it?
I am not concerned if the cart is empty or not. I had to implement a template_redirect already, so when a user "removes" an item from the cart it redirects back to the cart rather than the home page. With:
add_action( 'template_redirect', function( ) {
$_var = ( $_GET[ 'removed_item' ] ) ?? null;
//var_dump($_var);
if( $_var == '1' ){
wp_safe_redirect( '/cart', 301 );
die();
}
} );
Can I hook into the same action? I don't see a query string after clicking the 'Undo' tho...
EDIT
I can see where the action is done. It's in the plugin includes/class-ws-form-handler.php, right around line 536... containing:
// Undo Cart Item
$cart_item_key = sanitize_text_field( $_GET['undo_item'] );
WC()->cart->restore_cart_item( $cart_item_key );
$referer = wp_get_referer() ? remove_query_arg( array( 'undo_item', '_wpnonce' ), wp_get_referer() ) : wc_get_cart_url();
wp_safe_redirect( $referer );
exit;
EDIT 2:
Adding the following to my redirection above does nothing...
// undo the remove, redirect back to cart
if ( ! empty( $_GET['undo_item'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'woocommerce-cart' ) ) {
// Undo Cart Item
$cart_item_key = sanitize_text_field( $_GET['undo_item'] );
WC()->cart->restore_cart_item( $cart_item_key );
wp_safe_redirect( wc_get_cart_url() );
exit;
}
So, when I var_dump wp_get_referer() I get back the home page of the site, which is not what I am looking for. I do not want to modify this file due to that not being that great of an idea... so, how can I just modify the action instead? The function itself is: public static function update_cart_action()
So... I ended up have to do some voodoo to get this to work.
Here is the hook...
// undo the remove, redirect back to cart
// check if the cart is updated... if it is, proceed
$_cu = apply_filters( 'woocommerce_update_cart_action_cart_updated', true );
if( $_cu ) {
global $woocommerce;
if ( ! empty( $_GET['undo_item'] ) && isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'woocommerce-cart' ) ) {
// item
$cart_item_key = sanitize_text_field( $_GET['undo_item'] );
// Get the cart
$woocommerce->cart->get_cart();
// restore the item
$woocommerce->cart->restore_cart_item( $cart_item_key );
// now do the redirect
wp_safe_redirect( wc_get_cart_url() );
exit;
}
}
It is applied in the init event
I have searched all over the internet, but it doesn't seem to be any answer anywhere in spite of countless people asking the question.
Is there any way to exclude a specific product from ALL coupons?
I understand that you can do it on coupon level, but this is rather messy especially as a lot of people have automatic coupons, several people creating coupons etc…
TLDR: Any way to make a product excluded from all coupons at Product level.
Here is a nice way to automate this process.
1) We add a custom checkbox in product general settings metabox to disable the coupon functionality for the current product. So you will get this in Backend Edit Product pages:
All selected products will be saved in an array and will be used in the following…
2) This selected products will be excluded from coupon discount at product level and the product discount amount will be set to zero.
The code:
// Create and display the custom field in product general setting tab
add_action( 'woocommerce_product_options_general_product_data', 'add_custom_field_general_product_fields' );
function add_custom_field_general_product_fields(){
global $post;
echo '<div class="product_custom_field">';
// Custom Product Checkbox Field
woocommerce_wp_checkbox( array(
'id' => '_disabled_for_coupons',
'label' => __('Disabled for coupons', 'woocommerce'),
'description' => __('Disable this products from coupon discounts', 'woocommerce'),
'desc_tip' => 'true',
) );
echo '</div>';;
}
// Save the custom field and update all excluded product Ids in option WP settings
add_action( 'woocommerce_process_product_meta', 'save_custom_field_general_product_fields', 10, 1 );
function save_custom_field_general_product_fields( $post_id ){
$current_disabled = isset( $_POST['_disabled_for_coupons'] ) ? 'yes' : 'no';
$disabled_products = get_option( '_products_disabled_for_coupons' );
if( empty($disabled_products) ) {
if( $current_disabled == 'yes' )
$disabled_products = array( $post_id );
} else {
if( $current_disabled == 'yes' ) {
$disabled_products[] = $post_id;
$disabled_products = array_unique( $disabled_products );
} else {
if ( ( $key = array_search( $post_id, $disabled_products ) ) !== false )
unset( $disabled_products[$key] );
}
}
update_post_meta( $post_id, '_disabled_for_coupons', $current_disabled );
update_option( '_products_disabled_for_coupons', $disabled_products );
}
// Make coupons invalid at product level
add_filter('woocommerce_coupon_is_valid_for_product', 'set_coupon_validity_for_excluded_products', 12, 4);
function set_coupon_validity_for_excluded_products($valid, $product, $coupon, $values ){
if( ! count(get_option( '_products_disabled_for_coupons' )) > 0 ) return $valid;
$disabled_products = get_option( '_products_disabled_for_coupons' );
if( in_array( $product->get_id(), $disabled_products ) )
$valid = false;
return $valid;
}
// Set the product discount amount to zero
add_filter( 'woocommerce_coupon_get_discount_amount', 'zero_discount_for_excluded_products', 12, 5 );
function zero_discount_for_excluded_products($discount, $discounting_amount, $cart_item, $single, $coupon ){
if( ! count(get_option( '_products_disabled_for_coupons' )) > 0 ) return $discount;
$disabled_products = get_option( '_products_disabled_for_coupons' );
if( in_array( $cart_item['product_id'], $disabled_products ) )
$discount = 0;
return $discount;
}
Code goes in function.php file of your active child theme (or active theme) or in any plugin file.
Tested and perfectly works
In WooCommerce I've implemented #jtsternberg's WooCommerce: Allow adding multiple products to the cart via the add-to-cart query string to allow adding multiple products at once, but I've received many complaints from customers who actually try to use one of the links containing multiple products.
For starters, if the customer clicks checkout and then clicks the browser "back" button, all the item quantities increment. I solved this by redirecting the user to the cart URL stripped of any additional parameters after the add-to-cart behavior completes, but it's not ideal.
What I really want is to check if the item is in the cart first and only add to cart if it isn't there already. Has anyone done something similar?
Working Update:
I ended up modifying the code from #jtsternberg to use a completely separate param name in order to avoid conflict with the default add-to-cart behavior. Then I was able to use #LoicTheAztec's suggested code below by wrapping the behavior in a check to see if that new param exists. Here's the full section:
function custom_product_link() {
if (empty( $_REQUEST['multi-product-add'])) {
return;
}
$product_ids = explode( ',', $_REQUEST['multi-product-add'] );
foreach ( $product_ids as $product_id ) {
$product_id = apply_filters( 'woocommerce_add_to_cart_product_id', absint( $product_id ) );
$was_added_to_cart = false;
$adding_to_cart = wc_get_product( $product_id );
if ( ! $adding_to_cart ) {
continue;
}
$add_to_cart_handler = apply_filters( 'woocommerce_add_to_cart_handler', $adding_to_cart->product_type, $adding_to_cart );
if ( 'simple' !== $add_to_cart_handler ) {
continue;
}
// For now, quantity applies to all products.. This could be changed easily enough, but I didn't need this feature.
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( $_REQUEST['quantity'] );
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity );
if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity ) ) {
wc_add_to_cart_message( array( $product_id => $quantity ), true );
}
}
if ( wc_notice_count( 'error' ) === 0 ) {
// If has custom URL redirect there
if ( $url = apply_filters( 'woocommerce_add_to_cart_redirect', false ) ) {
wp_safe_redirect( $url );
exit;
} elseif ( get_option( 'woocommerce_cart_redirect_after_add' ) === 'yes' ) {
wp_safe_redirect( wc_get_cart_url() );
exit;
}
}
}
function check_product_added_to_cart( $passed, $product_id, $quantity) {
if (!empty( $_REQUEST['multi-product-add'])) {
foreach (WC()->cart->get_cart() as $cart_key => $cart_item ){
// if products are already in cart:
if( $cart_item['product_id'] == $product_id ) {
// Set the verification variable to "not passed" (false)
$passed = false;
// (Optionally) Displays a notice if product(s) are already in cart
// wc_add_notice( '<strong>' . $btn['label'] . '</strong> ' . __( 'This product is already in your cart.', 'woocommerce' ), 'error' );
// Stop the function returning "false", so the products will not be added again
return $passed;
}
}
}
return $passed;
}
add_action( 'wp_loaded', 'custom_product_link', 15 );
add_action( 'woocommerce_add_to_cart_validation', 'check_product_added_to_cart', 10, 3 );
As in the code you are using you have woocommerce_add_to_cart_validation filter hook inside it, you can use it in a custom hooked function to check if products rare already in cart with something like:
add_action( 'woocommerce_add_to_cart_validation', 'check_product_added_to_cart', 10, 3 );
function check_product_added_to_cart( $passed, $product_id, $quantity) {
foreach (WC()->cart->get_cart() as $cart_key => $cart_item ){
// if products are already in cart:
if( $cart_item['data']->get_id() == $product_id ) {
// Set the verification variable to "not passed" (false)
$passed = false;
// (Optionally) Displays a notice if product(s) are already in cart
wc_add_notice( '<strong>' . $btn['label'] . '</strong> ' . __( 'This product is already in your cart.', 'woocommerce' ), 'error' );
// Stop the function returning "false", so the products will not be added again
return $passed;
}
}
return $passed;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Code is tested and this normally should works with your customization…
For product quantities, you can use the $quantity argument with $cart_item['quantity'] in some conditions…
I would like to show the value of my custom field created with Advanced Custom Fields plugin, at the same time in WooCommerce cart and checkout pages.
I'm using the code below in the functions.php page of my theme, which displays only in the single page of the product:
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_field', 0 );
function add_custom_field() {
global $post;
echo "<div class='produto-informacoes-complementares'>";
echo get_field( 'info_complementar', $product_id, true );
echo "</div>";
return true;
}
Thank you all in advanced for all the help given and excuse my english.
I can't test this on your web shop, so I am not completely sure:
Displaying custom field value in single product page (your function):
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_field', 0 );
function add_custom_field() {
global $product; // Changed this
// Added this too (compatibility with WC +3)
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
echo "<div class='produto-informacoes-complementares'>";
echo get_field( 'info_complementar', $product_id );
echo "</div>";
return true;
}
(updated) Storing this custom field into cart and session:
add_filter( 'woocommerce_add_cart_item_data', 'save_my_custom_product_field', 10, 2 );
function save_my_custom_product_field( $cart_item_data, $product_id ) {
$custom_field_value = get_field( 'info_complementar', $product_id, true );
if( !empty( $custom_field_value ) )
{
$cart_item_data['info_complementar'] = $custom_field_value;
// below statement make sure every add to cart action as unique line item
$cart_item_data['unique_key'] = md5( microtime().rand() );
}
return $cart_item_data;
}
(updated) Render meta on cart and checkout:
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout', 10, 2 );
function render_meta_on_cart_and_checkout( $cart_data, $cart_item ) {
$custom_items = array();
// Woo 2.4.2 updates
if( !empty( $cart_data ) ) {
$custom_items = $cart_data;
}
if( isset( $cart_item['info_complementar'] ) ) {
$custom_items[] = array( "name" => "Info complementar", "value" => $cart_item['info_complementar'] );
}
return $custom_items;
}
There was some mistakes in the 2 last hooks that was making this not working… Now it should work.