Woocommerce Product publish, update and delete hooks - wordpress

I need Woocommerce Product publish, update and delete hooks if any one know then please inform me.
I find this hook :
add_action('transition_post_status', 'wpse_110037_new_posts', 10, 3);
function wpse_110037_new_posts($new_status, $old_status, $post) {
if(
$old_status != 'publish'
&& $new_status == 'publish'
&& !empty($post->ID)
&& in_array( $post->post_type,
array( 'product')
)
) {
//add some cde here
}
}
but it's only display product id, title, publish status etc....but i want product price, category, tag, brand and stock status.
So please replay me if any one know.
Thanks,
Ketan.

Woocommerce Products are basically wordpress posts. You can use wordpress hooks
add_action( 'before_delete_post', 'wpse_110037_new_posts' );
add_action( 'save_post', 'wpse_110037_new_posts' );
function wpse_110037_new_posts($post_id){
$WC_Product = wc_get_product( $post_id);
}
wc_get_product() will return WC_Product object and you can get the product details from it.

I Prefer to check if the status if not a draft. Also you can have the third parameter updateto check if it's an update or not
add_action( 'save_post', array($this, 'wpse1511_create_or_update_product' ), 10, 3);
function wpse1511_create_or_update_product($post_id, $post, $update){
if ($post->post_status != 'publish' || $post->post_type != 'product') {
return;
}
if (!$product = wc_get_product( $post )) {
return;
}
// Make something with $product
// You can also check $update
}

The save_post and save_post_product hooks run before the post_meta is updated and since most of the WooCommerce product data is stored as post_meta, using them might cause issues.
Thankfully, since v3, there are specific WooCommerce hooks that run after a product is updated (woocommerce_update_product) and when a product is created (woocommerce_new_product).
add_action( 'woocommerce_new_product', 'on_product_save', 10, 1 );
add_action( 'woocommerce_update_product', 'on_product_save', 10, 1 );
function on_product_save( $product_id ) {
$product = wc_get_product( $product_id );
// do something with this product
}

This hook will run after WC has updated the product in the DB:
add_action('save_post_product', 'ns_sync_on_product_save', 10, 3);
function ns_sync_on_product_save( $post_id, $post, $update ) {
$product = wc_get_product( $post_id );
}

Related

Redirecting customers to custom page after sale in WooCommerce

I'm trying to learn WooCommerce and WordPress plugins so I'm tweaking around. I'm trying to create a plugin that redirects customer to a custom page after checkout. The custom page/url can be defined when I create the product. Here is my code:
<?php
/*
Plugin Name: Custom Redirect After Sale
Description: Redirects customers to a custom page after a successful sale.
*/
// Register a new meta field for products
add_action( 'add_meta_boxes', 'custom_redirect_meta_box' );
function custom_redirect_meta_box() {
add_meta_box( 'custom_redirect_meta_box', 'Custom Redirect URL', 'custom_redirect_meta_box_callback', 'product', 'side' );
}
function custom_redirect_meta_box_callback( $post ) {
$value = get_post_meta( $post->ID, '_custom_redirect_url', true );
echo '<label for="custom_redirect_url">Custom Redirect URL:</label>';
echo '<input type="text" id="custom_redirect_url" name="custom_redirect_url" value="' . esc_attr( $value ) . '" style="width:100%">';
}
// Save the meta field value when the product is saved
add_action( 'save_post_product', 'save_custom_redirect_meta_box_data' );
function save_custom_redirect_meta_box_data( $post_id ) {
if ( isset( $_POST['custom_redirect_url'] ) ) {
update_post_meta( $post_id, '_custom_redirect_url', sanitize_text_field( $_POST['custom_redirect_url'] ) );
}
}
// Redirect to the custom page after a successful sale
add_action( 'woocommerce_payment_complete', 'custom_redirect_after_sale' );
function custom_redirect_after_sale( $order_id ) {
$order = wc_get_order( $order_id );
//$order->update_status( 'completed' );
$items = $order->get_items();
// Get the first product in the order
$product = reset($items);
// Get the custom redirect URL for the product
//$redirect_url = get_post_meta( $product->get_product_id(), '_custom_redirect_url', true );
$redirect_url = get_post_meta( $product->get_id(), '_custom_redirect_url', true );
//echo "Meta retrieved: " . $redirect_url;
//error_log("callback fired");
//echo "Payment complete ho ho ho";
if( $redirect_url ) {
wp_redirect( $redirect_url );
exit;
}
}
It seems the woocommerce_payment_complete hook is not firing. I tried to echo out the redirect url and text but it doesn't seem to work.
I'm on localhost and I'm using the cash on delivery payment method.
Basing this answer on the great https://rudrastyh.com/ - specifically this tutorial https://rudrastyh.com/woocommerce/thank-you-page.html#redirects this is the code that should work for what you are trying to do.
First, you hook into the template_redirect action to determine the URL where the customer needs to go
Getting the Order ID, you can get the products purchased for that order
Once you have the purchased products, you can get their ID and meta data, the redirect URL you saved for each. Note that while you use WP functions for handling meta, when working with WooCommerce it is best practice to use its CRUD methods. In case in the future they port products to custom tables, your code will continue working.
Implement the redirect with the WP function wp_safe_redirect
Note that what you are trying to achieve will have problems if customers purchase orders with more than 1 product, and you have more than 1 redirect URL. In this implementation, the first product in the order that has a saved redirect URL will override all others
add_action( 'template_redirect', 'purchased_product_redirect');
function purchased_product_redirect(){
if( !function_exists( 'is_wc_endpoint_url' )){
return;
}
// do nothing if we are not on the order received page
if( ! is_wc_endpoint_url( 'order-received' ) || empty( $_GET[ 'key' ] ) ) {
return;
}
// Get the order ID
$order_id = wc_get_order_id_by_order_key( $_GET[ 'key' ] );
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get and Loop Over Order Items
foreach ( $order->get_items() as $item_id => $item ) {
$product_id = $item->get_product_id();
$product = wc_get_product($product_id);
if(!$product){
continue;
}
//Get the first product's redirect URL
$product_redirect_url = $product->get_meta('_custom_redirect_url');
if(!$product_redirect_url){
continue;
}
wp_safe_redirect( $product_redirect_url );
exit; // always exit after using wp_safe_redirect
}
}

Can't pass custom cart item data to WC()->cart->add_to_cart() woocommerce 4.9

I'm working on woocommerce API plugin development and trying to pass custom cart item data using the below code in add to cart API endpoint.
$cart_item_key = WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations, array('margin' => 200));
and want to use that custom cart item data on woocommerce_before_calculate_totals hook (see code below) but can't getting custom cart item data ($cart_item['margin']) there.
add_action( 'woocommerce_before_calculate_totals', 'custom_cart_item_price', 30, 1 );
function custom_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( $cart->get_cart() as $cart_item ) {
if( isset($cart_item['margin']) ){
$final_price = ($cart_item['data']->get_price() + $cart_item['margin']);
$cart_item['data']->set_price($final_price);
}
}
}
I have installed Woocommerece 4.9 version, please help me to solve this issue.
Thanks in advance.
I had kind of the same problem, the issue was there was another plugin that might have a higher priority on the filter. It was a Role based pricing plugin, after deactivating it, everything worked. So just check if there is no other thing overwriting the function.
To do a test I used the woocommerce_add_to_cart_validation hook and I added a product to cart with add_to_cart() method of the WC_Cart class.
add_action( 'woocommerce_add_to_cart_validation', 'add_product_to_cart_programmatically', 10, 3 );
function add_product_to_cart_programmatically( $passed, $product_id, $quantity) {
$product_id = 166; // product id to add
$quantity = 10; // quantity product to add
WC()->cart->add_to_cart( $product_id, $quantity, 0, array(), array( 'margin' => 200 ) );
return $passed;
}
Once the product has been added to the cart, I can apply a custom price based on the custom cart item data:
add_action( 'woocommerce_before_calculate_totals', 'custom_cart_item_price', 30, 1 );
function custom_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( $cart->get_cart() as $cart_item ) {
if ( isset( $cart_item['margin'] ) && ! empty( $cart_item['margin'] ) ) {
$final_price = $cart_item['data']->get_price() + $cart_item['margin'];
$cart_item['data']->set_price( $final_price );
}
}
}
As recommended by #danielsalare you can try to increase the priority of the action, as in my example above.
I had a similar problem and found this solution: First, we add the custom products to Woocommerce. Second, we generate an identifier (ID), and later we add it.
for example, obtain product-id trow a with $_POST
$myProduct = $_POST['myProduct'];
$myProduct_id = WC()->cart->generate_cart_id($myProduct);
if (!WC()->cart->find_product_in_cart($myProduct_id )) {
WC()->cart->add_to_cart($myProduct);
}

WooCommerce Multivendor Marketplace get vendor id from product id

I am using WooCommerce Multivendor Marketplace plugin in Wordpress for multivendor site. I want to restrict a user so they can add only one vendor's product in their cart. I am writing a custom function to get vendor id.
I'm trying to use the get_wcfm_product_vendors function but it isn't working; I think that it might not be supported. I've search for other possible solutions but I could not find any. Is there any other method I can use to get vendor id for a product?
You may try WCFM single vendor checkout function, add this code to your child theme :
add_action( 'woocommerce_add_to_cart_validation', function( $is_allow, $product_id, $quantity ) {
$product = get_post( $product_id );
$product_author = $product->post_author;
//Iterating through each cart item
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$cart_product_id = $cart_item['product_id'];
$cart_product = get_post( $cart_product_id );
$cart_product_author = $cart_product->post_author;
if( $cart_product_author != $product_author ) {
$is_allow = false;
break;
}
}
if( !$is_allow ){
// We display an error message
wc_clear_notices();
wc_add_notice( __( "Well, you already have some item in your cart. First checkout with those and then purchase other items!", "wcfm" ), 'error' );
}
return $is_allow;
}, 50, 3 );
https://docs.wclovers.com/tweaks/#single-vendor-checkout

Woocommerce hook that fires after a product is updated via REST API

I am using this hook to run some code after the product is updated:
add_action( 'updated_post_meta', 'attach_variation_images_on_product_save', 10, 4 );
function attach_variation_images_on_product_save( $meta_id, $post_id, $meta_key, $meta_value ) {
if ( $meta_key == '_edit_lock' ) {
if ( get_post_type( $post_id ) == 'product' ) {
//do something
}
}
}
This is working as expected, the function is executed after the product is updated. I want to run the same function when a product is getting updated via the REST API. I hooked my function to woocommerce_rest_insert_product_object like this but it did not work:
add_action( 'woocommerce_rest_insert_product_object', 'attach_variation_images_on_product_update_via_rest', 10, 3 );
function attach_variation_images_on_product_update_via_rest( $post, $request, $true ) {
if ( get_post_type( $post ) == 'product' ) {
$product = wc_get_product( $post );
//do something
}
}
Am I not using the right hook? Is there another hook I can use?
EDIT 1: It seems that my code was not running because get_post_type($post) is type of post and not product. I am trying to attach an image to variations using add_post_meta($variation_id, '_thumbnail_id', $image_id); inside a loop. It seems the function attach_variation_images_on_product_update_via_rest( $post, $request, $true ) is executed till the end but it does not attach the image to the variations.
I ended up using this hook add_action( 'woocommerce_update_product', 'my_callback_function', 10, 1 ); that executes the callback function when you updating products from Woocommerce back-end as well from the REST API.

Need Woocommerce to only allow 1 product in the cart. If a product is already in the cart and another 1 is added then it should remove the previous 1

I think this code should work but not exactly sure where to place it. Everywhere I have tried has failed so far...
add_action('init', 'woocommerce_clear_cart');
function woocommerce_clear_cart() {
global $woocommerce, $post, $wpdb;
$url = explode('/', 'http://'.$_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
$slug=$url[4];
$postid = $wpdb->get_var("SELECT ID FROM $wpdb->posts WHERE post_status='publish' AND post_name = '$slug'");
if ($postid){
if ($postid == PRODUCTID1 || $postid == PRODUCTID2){
$woocommerce->cart->empty_cart();
}
}
}
Unfortunately there is no 'action' hook before WooCommerce adds an item to the cart. But they have a 'filter' hook before adding to cart.
That is how I use it:
add_filter( 'woocommerce_add_cart_item_data', 'woo_custom_add_to_cart' );
function woo_custom_add_to_cart( $cart_item_data ) {
global $woocommerce;
$woocommerce->cart->empty_cart();
// Do nothing with the data and return
return $cart_item_data;
}
Based on the accepted answer and the latest Woo version 2.5.1 I updated the function to be slightly cleaner using the code woo uses in class-wc-checkout.php to clear the cart
add_filter( 'woocommerce_add_cart_item_data', '_empty_cart' );
function _empty_cart( $cart_item_data ) {
WC()->cart->empty_cart();
return $cart_item_data;
}
There is a filter/hook that runs before an item is added to the cart as each product goes through validation before it is added.
So when validating a product, we can check if the item if there are already items in the cart and clears those (if the current item is able to be added) and adds an error message.
/**
* When an item is added to the cart, remove other products
*/
function so_27030769_maybe_empty_cart( $valid, $product_id, $quantity ) {
if( ! empty ( WC()->cart->get_cart() ) && $valid ){
WC()->cart->empty_cart();
wc_add_notice( 'Whoa hold up. You can only have 1 item in your cart', 'error' );
}
return $valid;
}
add_filter( 'woocommerce_add_to_cart_validation', 'so_27030769_maybe_empty_cart', 10, 3 );
This worked like a charm for me, removes the previous product and adds the new one with the new product configuration. Cheers
Update: For WooCommerce 3.0.X
function check_if_cart_has_product( $valid, $product_id, $quantity ) {
if(!empty(WC()->cart->get_cart()) && $valid){
foreach (WC()->cart->get_cart() as $cart_item_key => $values) {
$_product = $values['data'];
if( $product_id == $_product->get_id() ) {
unset(WC()->cart->cart_contents[$cart_item_key]);
}
}
}
return $valid;
}
add_filter( 'woocommerce_add_to_cart_validation', 'check_if_cart_has_product', 10, 3 );
For WooCommerce version less than 3.0.X
function check_if_cart_has_product( $valid, $product_id, $quantity ) {
if(!empty(WC()->cart->get_cart()) && $valid){
foreach (WC()->cart->get_cart() as $cart_item_key => $values) {
$_product = $values['data'];
if( $product_id == $_product->id ) {
unset(WC()->cart->cart_contents[$cart_item_key]);
}
}
}
return $valid;
}
add_filter( 'woocommerce_add_to_cart_validation', 'check_if_cart_has_product', 10, 3 );
You have two options:
WooCommerce Min/Max Quantities extension
The following code added to your functions.php theme file
add_filter ( 'woocommerce_before_cart' , 'allow_single_quantity_in_cart' );
function allow_single_quantity_in_cart() {
global $woocommerce;
$cart_contents = $woocommerce->cart->get_cart();
$keys = array_keys ( $cart_contents );
foreach ( $keys as $key ) {
$woocommerce->cart->set_quantity ( $key, 1, true );
}
}
Don't add functions directly to your commerce files...you'll lose all your code when you update.
User functions should always be hooked through the functions.php of your theme.
Add this to your child-themes functions.php (tested in 2022, requires PHP 7 or higher):
add_filter('woocommerce_add_cart_item_data', function($cart_data) {
wc_empty_cart();
return $cart_data;
}, 99);
The "woocommerce_add_cart_item_data" filter fires every time a new item is added to the cart. We use this chance to call "wc_empty_cart" which empties the cart and optionally the persistent cart too. The new items is therefore alone in the cart.
Try this,
For removing the all products from the cart and adding the last added one,
//For removing all the items from the cart
global $woocommerce;
$woocommerce->cart->empty_cart();
$woocommerce->cart->add_to_cart($product_id,$qty);
class file is wp-content/plugins/woocommerce/classes/class-wc-cart.php.
You can add the above code on the add to cart function in functions.php.
Hope its works..
Eu coloco na function.php do tema, o que faço ou no woocomerce
function check_if_cart_has_product( $valid, $product_id, $quantity ) {
if(!empty(WC()->cart->get_cart()) && $valid){
foreach (WC()->cart->get_cart() as $cart_item_key => $values) {
$_product = $values['data'];
if( $product_id == $_product->id ) {
wc_add_notice( 'The product is already in cart', 'error' );
return false;
}
}
}
return $valid;
}
add_filter( 'woocommerce_add_to_cart_validation', 'check_if_cart_has_product', 10, 3 );
On WordPress 5.6.1. no need to add a custom filter or write code there is an option to limit one product add to the cart.

Resources