Can't add to cart (custom function) when not logged - wordpress

this function was working fine but since few days (and few updates Wp 5.9 to 6.X And others plugins) it only work when i'm logged in!.. For normal Users. the form post but the Cart is empty.. This function is used to add in bulk one ore more variation of the same product..
The function is in my child theme and was working for 1 year.. unfortunately.. i can't find a backup from before my updates!?! SHAME ON ME!!
function cps_add_multiple_products_to_cart() {
if ( empty( $_REQUEST['add-to-cart-manual'] )) {
return;
}
global $woocommerce;
$active_step = active_step();
$step_urls = step_urls();
//'add-to-cart' should be in this format - product_id1|variation_id1|variation_attribute1|variation_value1,product_id2|variation_id2|variation_attribute2|variation_value2 ... this is the format - product_id1|variation_id1|variation_attribute1|variation_value1,product_id2|variation_id2|variation_attribute2|variation_value2
$product_var_ids = explode( ',', $_REQUEST['add-to-cart-manual'] ); //product with variation ids that needs to be added
foreach ( $product_var_ids as $product_var_id ) {
$pdt_var_temp = explode( '|', $product_var_id );
$product_id = $pdt_var_temp[0];
$variation_id = $pdt_var_temp[1];
$variation_attribute = $pdt_var_temp[2];
$variation_attribute_value = $pdt_var_temp[3];
$variation_qte = $pdt_var_temp[4];
$pdt_in_cart = false;
foreach ( $woocommerce->cart->cart_contents as $key => $values ) {
if( in_array( $values['variation_id'], $variation_id ) ) {
$pdt_in_cart = true;
}
}
if ( !$pdt_in_cart ) {
$quantity = $variation_qte;
if ($quantity > 0) {
$variation = array( $variation_attribute => $variation_attribute_value );
$woocommerce->cart->add_to_cart( $product_id, $quantity, $variation_id, $variation );
}
}
}
}
add_action( 'woocommerce_before_main_content', 'cps_add_multiple_products_to_cart', 15 );

Related

Add free product when a certain coupon is applied in WooCommerce

I can add a product to cart, when a certain coupon is used via the woocommerce_applied_coupon hook and the add_to_cart() function
add_action('woocommerce_applied_coupon', 'apply_product_on_coupon');
function apply_product_on_coupon( ) {
global $woocommerce;
$coupon_id = 'mybday';
$free_product_id = 131468;
if(in_array($coupon_id, $woocommerce->cart->get_applied_coupons())){
$woocommerce->cart->add_to_cart($free_product_id, 1);
}
}
My question: is there a way to also discount it to 0 in the same callback function?
Your current code contains some mistakes or could be optimized:
global $woocommerce can be replaced by WC()
$woocommerce->cart->get_applied_coupons() is not necessary, as the coupon that have been applied is passed to the callback function.
Instead, use the last available argument in WC_Cart::add_to_cart() method, which will allow you to add any custom cart item data. Then you will be able to get that data easily from the cart object.
So you get:
function action_woocommerce_applied_coupon( $coupon_code ) {
// Settings
$product_id = 131468;
$quantity = 1;
$free_price = 0;
$coupon_codes = array( 'coupon1', 'mybday' );
// Compare
if ( in_array( $coupon_code, $coupon_codes ) ) {
// Add product to cart
WC()->cart->add_to_cart( $product_id, $quantity, 0, array(), array( 'free_price' => $free_price ) );
}
}
add_action( 'woocommerce_applied_coupon', 'action_woocommerce_applied_coupon', 10, 1 );
// Set free price from custom cart item data
function action_woocommerce_before_calculate_totals( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 ) return;
// Loop through cart contents
foreach ( $cart->get_cart_contents() as $cart_item ) {
if ( isset( $cart_item['free_price'] ) ) {
$cart_item['data']->set_price( $cart_item['free_price'] );
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'action_woocommerce_before_calculate_totals', 10, 1 );
Note: in addition to using woocommerce_applied_coupon, you must also use woocommerce_removed_coupon for when the coupon is removed, the product is removed as well
function action_woocommerce_removed_coupon( $coupon_code ) {
// Settings
$product_id = 131468;
$coupon_codes = array( 'coupon1', 'mybday' );
// Compare
if ( in_array( $coupon_code, $coupon_codes ) ) {
// Loop through cart contents
foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {
// When product in cart
if ( $cart_item['product_id'] == $product_id ) {
// Remove cart item
WC()->cart->remove_cart_item( $cart_item_key );
break;
}
}
}
}
add_action( 'woocommerce_removed_coupon', 'action_woocommerce_removed_coupon', 10, 1 );

How to add ACF field to custom column on WooCommerce admin orders list

ACF is set up for post type on WooCommerce products. However, I am trying to add a custom column to WooCommerce orders list within Admin dashboard and add the products ACF field.
I have added the column to display after order_status, but I'm having problems getting the ACF field to display.
// ADD NEW COLUMN
add_filter( 'manage_edit-shop_order_columns', 'custom_shop_order_column', 20 );
function custom_shop_order_column($columns)
{
$reordered_columns = array();
foreach( $columns as $key => $column){
$reordered_columns[$key] = $column;
if( $key == 'order_status' ){
$reordered_columns['my-column'] = __( 'Location','theme_domain');
}
}
return $reordered_columns;
}
Here, adding ACF to new colum.
// ADD ACF FIELD TO COLUMN
add_action( 'manage_shop_order_posts_custom_column' , 'custom_orders_list_column_content', 20, 2 );
function custom_orders_list_column_content( $column, $post_id )
{
if ( 'Location' == $column_name ){
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
echo get_field( 'location', $product_id );
}
return true;
}
Still learning and not sure how to do this, any advice?
An order generally consists of several products, therefore you cannot use $product_id directly, but you have to loop through the order items.
So you get:
/**
* Add columns
*/
function filter_manage_edit_shop_order_columns( $columns ) {
$reordered_columns = array();
foreach ( $columns as $key => $column ) {
$reordered_columns[$key] = $column;
if ( $key == 'order_status' ) {
$reordered_columns['my-column'] = __( 'Location','theme_domain' );
}
}
return $reordered_columns;
}
add_filter( 'manage_edit-shop_order_columns', 'filter_manage_edit_shop_order_columns', 10, 1 );
/**
* Populate columns
*/
function filter_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'my-column' ) {
// Get order
$order = wc_get_order( $post_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Get items
$items = $order->get_items();
// Loop through
foreach ( $items as $key => $item ) {
// Product ID
$product_id = $item->get_variation_id() > 0 ? $item->get_variation_id() : $item->get_product_id();
// Get field
$address = get_field( 'location', $product_id );
// Output
echo ($address) ? '<div>Address: ' . $address . '</div>' : '<div>Address: No address found!</div>';
}
}
}
}
add_filter( 'manage_shop_order_posts_custom_column', 'filter_manage_shop_order_posts_custom_column', 10, 2 );

Update WooCommerce order status if product custom field is set

I need to automatically set a certain order status (different than processing) when getting a new order.
This is achieved by this function:
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) { return; }
$order = wc_get_order( $order_id );
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
This totally works. Now I only need this to happen when a product has a customization.
The way to customize a product is filling an input field before adding to cart. The input is attached to the item data:
// Add custom cart item data
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 10, 2 );
function add_custom_cart_item_data( $cart_item_data, $product_id ){
if( isset($_POST['custom_text']) ) {
$cart_item_data['custom_text'] = sanitize_text_field( $_POST['custom_text'] );
$cart_item_data['unique_key'] = md5( microtime().rand() ); // Make each item unique
}
return $cart_item_data;
}
Then the custom text is retrieved and displayed in cart and in the order data using this:
// Display custom cart item data on cart and checkout
add_filter( 'woocommerce_get_item_data', 'display_custom_cart_item_data', 10, 2 );
function display_custom_cart_item_data( $cart_item_data, $cart_item ) {
if ( !empty( $cart_item['custom_text'] ) ){
$cart_item_data[] = array(
'name' => __('Customization', 'woocommerce'),
'value' => $cart_item['custom_text'] // Already sanitized field
);
}
return $cart_item_data;
}
// Save and display custom item data everywhere on orders and email notifications
add_action( 'woocommerce_checkout_create_order_line_item', 'add_product_custom_field_as_order_item_meta', 10, 4 );
function add_product_custom_field_as_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( isset($values['custom_text']) ) {
$item->update_meta_data('Add on', $values['custom_text'] );
}
}
I'm trying using the if ( isset($values['custom_text']) ) part as a trigger of the function to change the order status only if the product add on is set and other similar methods (like if ( !empty( $cart_item['custom_text'] ) ) but I'm not sure this is the way to go:
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) {return;}
$order = wc_get_order( $order_id );
if ( isset($values['custom_text']) ) {
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
}
This above does nothing. Am I anywhere near it with this approach?
EDIT: I tried this too
add_action('woocommerce_thankyou','change_order_status');
function change_order_status( $order_id ) {
if ( ! $order_id ) {return;}
$order = wc_get_order( $order_id );
foreach ( $order->get_items() as $item_id => $item ) {
$allmeta = $item->get_meta_data();
if ( isset($values['custom_text']) ) {
if( 'processing'== $order->get_status() ) {
$order->update_status( 'wc-custom-status' );
}
}
}
}
Your code contains some unnecessary steps as well as some shortcomings
For example, the woocommerce_add_cart_item_data hook contains 3 arguments versus 2
So you get:
// Add custom cart item data
function filter_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
// Isset and NOT empty
if ( isset ( $_POST[ 'custom_text' ] ) && ! empty ( $_POST[ 'custom_text' ] ) ) {
$cart_item_data['custom_text'] = sanitize_text_field( $_POST['custom_text'] );
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'filter_add_cart_item_data', 10, 3 );
// Display custom cart item data on cart and checkout
function filter_woocommerce_get_item_data( $cart_data, $cart_item = null ) {
if ( isset ( $cart_item['custom_text'] ) ) {
$cart_data[] = array(
'name' => __( 'Customization', 'woocommerce' ),
'value' => $cart_item['custom_text']
);
}
return $cart_data;
}
add_filter( 'woocommerce_get_item_data', 'filter_woocommerce_get_item_data', 10, 2 );
// Add the information as meta data so that it can be seen as part of the order
// Save and display custom item data everywhere on orders and email notifications
function action_woocommerce_checkout_create_order_line_item( $item, $cart_item_key, $values, $order ) {
if ( isset ( $values['custom_text'] ) ) {
$item->update_meta_data( 'custom_text', $values['custom_text'] );
}
}
add_action( 'woocommerce_checkout_create_order_line_item', 'action_woocommerce_checkout_create_order_line_item', 10, 4 );
// On thankyou page
function action_woocommerce_thankyou( $order_id ) {
// Get $order object
$order = wc_get_order( $order_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
// Only when the current order status is 'processing'
if ( $order->get_status() == 'processing' ) {
// Loop trough
foreach ( $order->get_items() as $item ) {
// Get meta
$value = $item->get_meta( 'custom_text' );
// NOT empty
if ( ! empty ( $value ) ) {
// Update status (change to desired status)
$order->update_status( 'cancelled' );
// Stop loop
break;
}
}
}
}
}
add_action( 'woocommerce_thankyou', 'action_woocommerce_thankyou', 10, 1 );
Currently it is just checking if the value exists, to compare it effectively
Change
// NOT empty
if ( ! empty ( $value ) ) {
// Update status
$order->update_status( 'cancelled' );
// Stop loop
break;
}
To
// Compare
if ( $value == 'some value' ) ) {
// Update status
$order->update_status( 'cancelled' );
// Stop loop
break;
}

Sort WooCommerce cart by category

I already found a snippet to sort the checkout cart alphabetically. This works perfect but as mentioned I try to sort and group my products by category.
Is there anyone who could tweak the following snippet so it sorts the products by category?
add_action( 'woocommerce_cart_loaded_from_session', 'bbloomer_sort_cart_items_alphabetically' );
function bbloomer_sort_cart_items_alphabetically() {
global $woocommerce;
// READ CART ITEMS
$products_in_cart = array();
foreach ( $woocommerce->cart->cart_contents as $key => $item ) {
$products_in_cart[ $key ] = $item['data']->get_title();
}
// SORT CART ITEMS
natsort( $products_in_cart );
// ASSIGN SORTED ITEMS TO CART
$cart_contents = array();
foreach ( $products_in_cart as $cart_key => $product_title ) {
$cart_contents[ $cart_key ] = $woocommerce->cart->cart_contents[ $cart_key ];
}
$woocommerce->cart->cart_contents = $cart_contents;
}
In order to grouped cart items by categories, add follows code snippet -
function woocommerce_before_cart_contents(){
global $woocommerce;
$cat_wisw_pros = array();
foreach ( $woocommerce->cart->get_cart() as $cart_item_key => $cart_item ) {
$product_id = $cart_item['product_id'];
$cat_ids = wp_get_post_terms( $product_id, 'product_cat', array( 'fields' => 'ids' ) );
foreach ( $cat_ids as $id ) {
$cat_wisw_pros[$id][$cart_item_key] = $cart_item;
}
}
ksort( $cat_wisw_pros ); // Cat ID wise sort
$grouped_cart_items = array();
foreach ( $cat_wisw_pros as $cat_id => $cart_items ) {
foreach ( $cart_items as $cart_item_key => $cart_item ) {
if( !array_key_exists( $cart_item_key, $grouped_cart_items ) )
$grouped_cart_items[$cart_item_key] = $cart_item;
}
}
$woocommerce->cart->cart_contents = $grouped_cart_items;
}
add_action( 'woocommerce_before_cart_contents', 'woocommerce_before_cart_contents' );
You should be able to access the category ids with the method get_category_ids() as defined in the class WC_Product
$item['data']->get_category_ids()
but this gives you back an array, so you need to handle getting the id, e.g.
$ids = $item['data']->get_category_ids();
$id = $ids[ 0 ];
and transforming to a string, e.g.
$term_obj = get_term( $id, 'product_cat' );
$name = $term_obj->name;
you could then do
$products_in_cart[ $key ] = $name;
and by the same logic used in the code in your question sort by category.
There are several things to consider though, this does not handle items being in multiple categories, it just gets the first by using index 0. Additionally, this has no extra logic of sorting, if there are multiple products in the same category.
Please note, this is untested and merely an idea of an approach of how this could be handled.

Custom post type url rewriting

I am rewriting the url of CPT 'entertainment' to /entertainment/%year%/%monthnum%/%post_id%/ It is re-writing but gives 404 not found error. How can I solve this?
Here is my code:
add_action( 'init', 'my_rewrite');
function my_rewrite() {
global $wp_rewrite;
$entertainment_structure = '/entertainment/%year%/%monthnum%/%post_id%/';
$wp_rewrite->add_rewrite_tag("%entertainment%", '([^/]+)', "entertainment=");
$wp_rewrite->add_permastruct('entertainment', $entertainment_structure, false);
flush_rewrite_rules();
}
// Add filter to plugin init function
add_filter('post_type_link', 'entertainment_permalink', 10, 3);
// Adapted from get_permalink function in wp-includes/link-template.php
function entertainment_permalink($permalink, $post_id, $leavename) {
$post = get_post($post_id);
$rewritecode = array(
'%year%',
'%monthnum%',
'%day%',
'%hour%',
'%minute%',
'%second%',
$leavename? '' : '%post_id%',
'%post_id%',
'%category%',
'%author%',
$leavename? '' : '%post_id%',
);
if ( '' != $permalink && !in_array($post->post_status, array('draft', 'pending', 'auto-draft')) ) {
$unixtime = strtotime($post->post_date);
$category = '';
if ( strpos($permalink, '%category%') !== false ) {
$cats = get_the_category($post->ID);
if ( $cats ) {
usort($cats, '_usort_terms_by_ID'); // order by ID
$category = $cats[0]->slug;
if ( $parent = $cats[0]->parent )
$category = get_category_parents($parent, false, '/', true) . $category;
}
// show default category in permalinks, without
// having to assign it explicitly
if ( empty($category) ) {
$default_category = get_category( get_option( 'default_category' ) );
$category = is_wp_error( $default_category ) ? '' : $default_category->slug;
}
}
$author = '';
if ( strpos($permalink, '%author%') !== false ) {
$authordata = get_userdata($post->post_author);
$author = $authordata->user_nicename;
}
$date = explode(" ",date('Y m d H i s', $unixtime));
$rewritereplace =
array(
$date[0],
$date[1],
$date[2],
$date[3],
$date[4],
$date[5],
$post->ID,
$post->ID,
$category,
$author,
$post->ID,
);
$permalink = str_replace($rewritecode, $rewritereplace, $permalink);
} else { // if they're not using the fancy permalink option
}
return $permalink;
}
If I replace $entertainment_structure = '/entertainment/%year%/%monthnum%/%post_id%/'; with $entertainment_structure = '/entertainment/%year%/%monthnum%/%entertainment%/'; it works fine
You aren't supposed to hook init, but either register_activation_hook() it, if its a plugin or use after_switch_theme if its a theme (its important to either deactivate/reactivate the plugin or switch themes in order for your rewrites to apply). <= further read here
You might want to have a read of the rewrite API

Resources