woocommerce_update_product action – fire only once for every product update
Asked Answered
N

5

7

Which workaround is there for making the woocommerce_update_product action fire only once?

I've read that it fires twice because it needs to save once internally to retrieve an ID for images/variation saves.

But from a developer experience, this is really not what most need, I guess.

The only workaround I've found so far is to add the action and remove it in the hook directly:

add_action('woocommerce_update_product', 'my_product_update', 10, 2);
function my_product_update($product_id, $product){
    remove_action('woocommerce_update_product');
    // We'll get here only once!
}

However, this breaks when trying to do bulk edits, making it so that the hook only fires for the first product (because it gets removed afterwards!).

Which other way is there to work around this issue?

Thanks!

Navicert answered 30/12, 2019 at 8:41 Comment(2)
how about add custom meta in updated products? after update all products remove it.Pad
How would I know when to remove the custom meta data?Navicert
G
7

Maybe using WordPress Transient can help.

add_action('woocommerce_update_product', 'my_product_update', 10, 2);
function my_product_update($product_id, $product) {

    $updating_product_id = 'update_product_' . $product_id;
    if ( false === ( $updating_product = get_transient( $updating_product_id ) ) ) {
        // We'll get here only once! within 2 seconds for each product id;
        // run your code here!
        set_transient( $updating_product_id , $product_id, 2 ); // change 2 seconds if not enough
    }
}
Gemination answered 30/12, 2019 at 9:8 Comment(8)
Thanks for the answer. The only thing I'm worrying about is actual updates coming in faster than the specified time.Navicert
would that actually happen? like updating a single product more than once in 2 seconds? it can but I don't think it would practically.Gemination
Yeah, that's a good point. In there it could, but it's probably unlikely (and may be worth ignoring). Thanks for bringing that up!Navicert
you can even set it to 1 second... It should work if you put set_transient at the very last line to run... like how it is right now...Gemination
Yep, I did that. One more question about the code: why does the if-statement not just say: if (!get_transient($updating_product))? For me that seems to work fine.Navicert
depending on use. I have edited my answer to make it more clear. but yes, your if should work too...Gemination
Thanks for your help, I appreciate it!Navicert
This really helped me send push notifications about Product adds and updates using Firebase Cloud Messaging to a Flutter application because woocommerce_update_product hook runs twice and two notifications were being sent.Kendricks
M
4

Use a global variable to do this in memory, no database entry required:

add_action('woocommerce_update_product', 'my_product_update', 10, 2);
function my_product_update($product_id, $product){
    global $previous_product_id;
    if ($previous_product_id === $product_id){
        // We'll get here only once (per product)!
    }
    $previous_product_id = $product_id;
}
Midvictorian answered 22/12, 2021 at 6:53 Comment(1)
This seems sweetest of the answers :)Pharmaceutics
C
1

i ran into the same issue (actually my hook was firing 5 times). I found a solution on the following page in the last comment.

Namely, the use of:

$times = did_action('woocommerce_update_product');
if( $times === 1){
    //  Do some stuff
}
Clarion answered 16/12, 2021 at 23:51 Comment(1)
What if someone updates multiple products in one request? This method does not check for product ID, so it will be only called once for all products, instead of once per productArtful
W
0

A simpler solution would be to set a $_POST variable and check at the start of the function.

add_action( 'woocommerce_update_product', 'my_action', 10, 1 );
function my_action(){
   if( isset($_POST['action_performed']) ){
      //Prevent running the action twice
      return;
    }
   
   // do you stuff here

   $_POST['action_performed'] = true;
}
Whitish answered 31/5, 2021 at 14:52 Comment(0)
E
0

An easy and fast solution is the use of the debug_backtrace() php function. The first time that is triggered uses debug_backtrace()[8]['class'] === "WC_Meta_Box_Product_Data". The second time uses (debug_backtrace()[8]['class'] === "WC_Meta_Box_Product_Images".

add_action( 'woocommerce_update_product', 'ml_update_product'));

public function ml_update_product($product_id)
{
    if(debug_backtrace()[8]['class'] === "WC_Meta_Box_Product_Images"){
    // Your code firing once
    }
}

Maybe not the best solution because it can change in the future with an Woocommerce update but for sure is the fastest and easiest solution.

Equinoctial answered 14/12, 2023 at 2:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.