WooCommerce custom code breaks after upgrade to 3.X - php

After the newer release of WooCommerce 3.X , the code written for WoCommerce 2.X doesn't work anymore without throwing error on the product type line.
function remove_bundled_items_from_cart( $cart_item_key, $instance ) {
if ( ! empty( $instance->removed_cart_contents[$cart_item_key] ) ) {
$product_id = $instance->removed_cart_contents[$cart_item_key]['product_id'];
$product_type = $instance->removed_cart_contents[$cart_item_key]['data']->product_type;
if ( $product_type == 'bundle' ) {
if (is_array($instance->cart_contents) || is_object($instance->cart_contents)) {
foreach ( $instance->cart_contents as $key => $cart_item ) {
if ( isset( $cart_item['is_bundled_item'] ) && $cart_item['is_bundled_item'] == $product_id ) {
WC()->cart->remove_cart_item( $key );
}
}
}
}
}
}
add_action( 'woocommerce_cart_item_removed', 'remove_bundled_items_from_cart', 10, 2 );
Does anybody know what I need to change to be able to have the $product_type stop throwing a error?
I would still need to be able to check if the string is "bundle", so I need somehow to get the information fetched.

As the main error in your code comes from:
$product_type = $instance->removed_cart_contents[$cart_item_key]['data']->product_type;
The $instance->removed_cart_contents[$cart_item_key]['data'] is the WC_Product Object instance and since WC versions 3+ WC_Product properties can't be accessed directly anymore.
Instead you will use the WC_Product method get_type() (or the conditional method is_type())…
As $instance->removed_cart_contents[$cart_item_key]['data'] doesn't exist in simple products, you need to test it in your first condition.
This should avoid throwing errors (on the product type line…):
add_action( 'woocommerce_cart_item_removed', 'remove_bundled_items_from_cart', 10, 2 );
function remove_bundled_items_from_cart( $cart_item_key, $instance ) {
if ( ! empty( $instance->removed_cart_contents[$cart_item_key]['data'] ) ) {
$product_id = $instance->removed_cart_contents[$cart_item_key]['product_id'];
$product = $instance->removed_cart_contents[$cart_item_key]['data']; // The WC_Product object
if ( $product->is_type( 'bundle' ) ) {
if (is_array($instance->cart_contents) || is_object($instance->cart_contents)) {
foreach ( $instance->cart_contents as $key => $cart_item ) {
if ( isset( $cart_item['is_bundled_item'] ) && $cart_item['is_bundled_item'] == $product_id ) {
WC()->cart->remove_cart_item( $key );
}
}
}
}
}
}
BUT I don't see in the raw cart data output any 'is_bundled_item' key…
Instead you can use in your code one of this available cart array keys:
bundled_by key (the parent WC_Bundle_Product cart item key)
bundled_item_id key (the child bundle item ID)
So you could replace in your code 'is_bundled_item' like:
add_action( 'woocommerce_cart_item_removed', 'remove_bundled_items_from_cart', 10, 2 );
function remove_bundled_items_from_cart( $cart_item_key, $instance ) {
if ( ! empty( $instance->removed_cart_contents[$cart_item_key]['data'] ) ) {
$product = $instance->removed_cart_contents[$cart_item_key]['data'];
if ( $product->is_type( 'bundle' ) ) {
if (is_array($instance->cart_contents) || is_object($instance->cart_contents)) {
foreach ( $instance->cart_contents as $key => $cart_item ) {
if ( isset( $cart_item['bundled_item_id'] ) && $cart_item['bundled_by'] == $cart_item_key ) {
WC()->cart->remove_cart_item( $key );
}
}
}
}
}
}
This should work totally…

Related

Show cash on delivery (COD) based on applied coupons in WooCommerce

I am trying to have cash on delivery (COD) enabled only for customers that use certain type off coupons.
I have an existing code that, when coupons of a certain type are entered, converts the product sales price to the regular product price.
Now I would like to add that cash on delivery (COD) only is available for valid coupons within the same function.
The part that I have tried to add is this:
if ($coupons = WC()->cart->get_applied_coupons() == False )
unset( $available_gateways['cod'] );
Resulting in:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price( $cart_object) {
global $woocommerce;
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
$coupon = False;
if ($coupons = WC()->cart->get_applied_coupons() == False )
unset( $available_gateways['cod'] );
if ($coupons = WC()->cart->get_applied_coupons() == False )
$coupon = False;
else {
foreach ( WC()->cart->get_applied_coupons() as $code ) {
$coupons1 = new WC_Coupon( $code );
if ($coupons1->type == 'percent_product' || $coupons1->type == 'percent')
$coupon = True;
}
}
if ($coupon == True)
foreach ( $cart_object->get_cart() as $cart_item )
{
$price = $cart_item['data']->regular_price;
$cart_item['data']->set_price( $price );
}
}
This does not give real error messages, but certainly not the desired result either. Any advice?
First of all I have rewritten your existing code, the part that converts sale prices to regular prices when a certain type of coupon is applied. This because your current code contains outdated methods:
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// Initialize
$flag = false;
// Applied coupons only
if ( sizeof( $cart->get_applied_coupons() ) >= 1 ) {
// Loop trough
foreach ( $cart->get_applied_coupons() as $coupon_code ) {
// Get an instance of the WC_Coupon Object
$coupon = new WC_Coupon( $coupon_code );
// Only for certain types, several can be added, separated by a comma
if ( in_array( $coupon->get_discount_type(), array( 'percent', 'fixed_product', 'percent_product' ) ) ) {
$flag = true;
break;
}
}
}
// True
if ( $flag ) {
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Get regular price
$regular_price = $cart_item['data']->get_regular_price();
// Set new price
$cart_item['data']->set_price( $regular_price );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );
Then to only make COD active, if coupons of a certain type are applied you can use the woocommerce_available_payment_gateways hook
By default COD will not be available:
// Payment gateways
function filter_woocommerce_available_payment_gateways( $payment_gateways ) {
// Not on admin
if ( is_admin() ) return $payment_gateways;
// Initialize
$flag = false;
// WC Cart
if ( WC()->cart ) {
// Get cart
$cart = WC()->cart;
// Applied coupons only
if ( sizeof( $cart->get_applied_coupons() ) >= 1 ) {
// Loop trough
foreach ( $cart->get_applied_coupons() as $coupon_code ) {
// Get an instance of the WC_Coupon Object
$coupon = new WC_Coupon( $coupon_code );
// Only for certain types, several can be added, separated by a comma
if ( in_array( $coupon->get_discount_type(), array( 'percent', 'fixed_product', 'percent_product' ) ) ) {
$flag = true;
break;
}
}
}
}
// NOT true, so false
if ( ! $flag ) {
// Cod
if ( isset( $payment_gateways['cod'] ) ) {
unset( $payment_gateways['cod'] );
}
}
return $payment_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'filter_woocommerce_available_payment_gateways', 10, 1 );
Both codes go goes in functions.php file of the active child theme (or active theme).
Tested in WordPress 5.8.1 and WooCommerce 5.8.0

Change price of product in WooCommerce cart and checkout

I'm creating a WooCommerce add-on to customize the product, based on selected options by the visitor and custom price is calculated and stored into session table also.
I am able to get that value in cart page also.
My problem: I would like to change the default price of the product and replace it with new calculated value in the WooCommerce process as cart, checkout, payment, mail notifications, order...
Any advice please?
Thanks
Ce right hook to get it working is woocommerce_before_calculate_totals. But you will have to complete (replace) the code to get the new price in the hooked function below:
add_action( 'woocommerce_before_calculate_totals', 'custom_cart_items_prices', 10, 1 );
function custom_cart_items_prices( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop Through cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Get the product id (or the variation id)
$product_id = $cart_item['data']->get_id();
// GET THE NEW PRICE (code to be replace by yours)
$new_price = 500; // <== Add your code HERE
// Updated cart item price
$cart_item['data']->set_price( $new_price );
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works on WooCommerce versions 3+. But as you don't give any code I can't test it for real getting the new price from session…
function save_subscription_wrap_data( $cart_item_data, $product_id ) {
$include_as_a_addon_subscription = get_field('include_as_a_addon_subscription',$product_id);
$subscricption_product_data = get_field('subscricption_product',$product_id);
$current_user = is_user_logged_in() ? wp_get_current_user() : null;
$subscriptions = wcs_get_users_subscriptions( $current_user->ID );
if($include_as_a_addon_subscription == "yes")
{
foreach ( $subscriptions as $subscription_id => $subscription ) {
$subscription_status = $subscription->get_status();
}
if($subscription_status == 'active')
{
$cart_item_data[ "subscribe_product" ] = "YES";
}
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_subscription_wrap_data', 99, 2 );
function render_meta_on_cart_and_checkout1( $cart_data, $cart_item = null ) {
$meta_items = array();
if( !empty( $cart_data ) ) {
$meta_items = $cart_data;
}
if( isset( $cart_item["subscribe_product"] ) ) {
$meta_items[] = array( "name" => "Product Type", "value" => "Package Addon" );
}
return $meta_items;
}
add_filter( 'woocommerce_get_item_data', 'render_meta_on_cart_and_checkout1', 100, 2 );
function calculate_gift_wrap_fee( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
$additionalPrice = 100;
foreach ( WC()->cart->get_cart() as $key => $value ) {
if( isset( $value["subscribe_product"] ) ) {
if( method_exists( $value['data'], "set_price" ) ) {
$orgPrice = floatval( $value['data']->get_price() );
//$value['data']->set_price( $orgPrice + $additionalPrice );
$value['data']->set_price(0);
} else {
$orgPrice = floatval( $value['data']->price );
//$value['data']->price = ( $orgPrice + $additionalPrice );
$value['data']->price = (0);
}
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 99 );

Determine language in functions.php [wpml]

I have function in functions.php , which automatically adds woocommerce product by ID to cart when website is visited.
The website is bilingual and the function can't determine translated product ID.
So, I want to know if is possible to add wpml function in functions.php, which determines first language of front end, then executes function, something like this:
<?php if(wpml_getLanguage()=='en'); ?>
---do function for product 22---
<?php elseif(wpml_getLanguage()=='it'); ?>
---do function for product 45--
<?php endif; ?>
My code:
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
$product_id = 22;
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
}
}
}
You can easily get this by using the native WPML variable ICL_LANGUAGE_CODE.
You can find more information about this topic on the following page:
https://wpml.org/documentation/support/wpml-coding-api/
This can be dropped into functions.php
add_action( 'init', 'add_product_on_language' );
function add_product_on_language(){
if ( ! is_admin() ) {
if( ICL_LANGUAGE_CODE == 'en' ){
$product_id = 22;
} elseif ( ICL_LANGUAGE_CODE == 'it' ) {
$product_id = 45;
}
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
}
}
}

Automatically add multiple products to cart on visit WooCommerce

I am working on a webshop test case and what I would like is to add automatically items/products to the cart on visit. So I searched for something like that when I found the same code over and over everywhere.
/*
* goes in theme functions.php or a custom plugin
**/
// add item to cart on visit
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
$product_id = 64;
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
}
}
}
From:
https://docs.woocommerce.com/document/automatically-add-product-to-cart-on-visit/
https://gist.github.com/kloon/2376300
So this works fine if you have only one product but I would like to add more than just 1 product. Is there someone with some PHP knowledge (and a some WordPress) that can help me out? Thanks in advance!
There are actually two ways you can do this, at least. You can either call the add_to_cart function more than once, or create a loop. Both ways are down here:
Method 1
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
$product_id = 64;
$found = false;
//check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
// if product not found, add it
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
// if no products in cart, add it
WC()->cart->add_to_cart( $product_id );
WC()->cart->add_to_cart( $product_id2 );
WC()->cart->add_to_cart( $product_id3 );
WC()->cart->add_to_cart( $product_id4 );
}
}
}
Method 2
foreach ($articles as $article) {
WC()->cart->add_to_cart( $article );
}
Do note that you should create a new array called articles holding all the IDs from the desired products. Another thing that is going to bother you then is checking whether the cart holds more than 0 items, and checking if all of them are in there.
Method 2 would look something like this:
add_action( 'template_redirect', 'add_product_to_cart' );
function add_product_to_cart() {
if ( ! is_admin() ) {
$articles = array(64);
$found = false;
// check if product already in cart
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if (($key = array_search($_product->id, $articles)) !== false)
unset($articles[$key]);
}
// if product not found, add it
if ( count($articles) > 0 ) {
foreach ($articles as $article) {
WC()->cart->add_to_cart($article);
}
}
} else {
// if no products in cart, add it
foreach ($articles as $article) {
WC()->cart->add_to_cart( $article );
}
}
}
}

Disabling Amazon Payments Advanced method from WooCommerce checkout page

I'm trying to disable a couple of payment gateways based on a user's role. The function & hook I found works on the Paypal method but not Amazon Payments Advanced. Here's my code:
function wk_disable_gateways( $available_gateways ) {
global $woocommerce;
$wholesale_cust = check_user_role( array( 'wholesale', 'orig-wholesale' ) );
if ( isset( $available_gateways['paypal'] ) && $wholesale_cust ) {
unset( $available_gateways['paypal'] );
}
if ( isset( $available_gateways['amazon_payments_advanced'] ) && $wholesale_cust ) {
unset( $available_gateways['amazon_payments_advanced'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'wk_disable_gateways' );
The "Pay with Amazon" code is still running on the checkout page. Any ideas?
This is not the best solution, but I have not been able to find a more viable answer.
First off I did what you did and disabled all gateways expect the one I wanted the user to use. In my case I only want someone to check out with the nmigateway.
This goes inside of your theme functions.php
add_filter( 'woocommerce_available_payment_gateways', 'filter_gateways', 1);
function filter_gateways( $gateways ){
global $woocommerce;
// what products you wish to exculde
$nonPPproducts = array(1457, 1447, 479); // LIST YOUR PRODUCT IDS HERE
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
if ( in_array( $values['product_id'], $nonPPproducts ) ) {
foreach ( $gateways as $gateway_key => $gateway ) {
if ( $gateway_key !== 'nmipay' ) {
unset( $gateways[ $gateway_key ] );
}
}
}
}
return $gateways;
}
Next here is the part that makes this not the best solution editing the plugins source code.
Change the following two functions inside of the plugins/woocommerce-gateway-amazon-payments-advanced/amazon-payments-advanced.php
/**
* Checkout Button
*
* Triggered from the 'woocommerce_proceed_to_checkout' action.
*/
function checkout_button() {
global $woocommerce;
// what products you wish to exculde
$nonPPproducts = array(1457, 1447, 479); // LIST YOUR PRODUCT IDS HERE
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
if ( in_array( $values['product_id'], $nonPPproducts ) ) {
$disable_button = true;
}
}
if(!isset($disable_button) && $disable_button !== true ){
?><div id="pay_with_amazon"></div><?php
}
}
/**
* Checkout Message
*/
function checkout_message() {
global $woocommerce;
// what products you wish to exculde
$nonPPproducts = array(1457, 1447, 479); // LIST YOUR PRODUCT IDS HERE
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
if ( in_array( $values['product_id'], $nonPPproducts ) ) {
$disable_button = true;
}
}
if(!isset($disable_button) && $disable_button !== true ){
if ( empty( $this->reference_id ) ) {
echo '<div class="woocommerce-info info"><div id="pay_with_amazon"></div> ' . apply_filters( 'woocommerce_amazon_pa_checkout_message', __( 'Have an Amazon account?', 'woocommerce-gateway-amazon-payments-advanced' ) ) . '</div>';
}
}
}
Keep in mind that when the plugin is updated all of your changes will be lost.

Categories