OhMad
OhMad

Reputation: 7289

woocommerce_update_product action – fire only once for every product update

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!

Upvotes: 8

Views: 5417

Answers (5)

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.

Upvotes: 0

Axos
Axos

Reputation: 188

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
}

Upvotes: 1

Design.Garden
Design.Garden

Reputation: 4207

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;
}

Upvotes: 5

Jahanzeb Khan
Jahanzeb Khan

Reputation: 19

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;
}

Upvotes: 0

Reigel Gallarde
Reigel Gallarde

Reputation: 65254

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
    }
}

Upvotes: 8

Related Questions