1
votes

I want to sent woocommerce product data after user create and save one, to web service of post company via soap-client. so i need a hook that fire after user create a product. i search a lot and find some word press and woocommerce hook but none of them do my work. after sending date,web service return an id that should be set as product sku. hear is my function code: (argument are from save_post_product hook)

function productPublished ($ID , $post , $update){
$product = wc_get_product( $post->ID);
$user = get_option ('myUsername');
$pass = get_option ('myPassword');
$name = $product->get_name();
$price = $product->get_regular_price();
$weight = $product->get_weight() * 1000;
$count = $product->get_stock_quantity();
$description = $product->get_short_description();
$result = $client -> AddStuff ([
    'username' => $user,
    'password' => $pass,
    'name' => $name,
    'price' => $price,
    'weight' => $weight, 
    'count' => $count,
    'description' => $description,
    'percentDiscount' => 0
]);
$stuff_id=(int)$result->AddStuffResult;
update_post_meta($product->get_id(),'_sku',$stuff_id);
}

i use save_post_product action. it seems like it fire after user click new product before entering name and price and etc. because default product data sent to web service and sku generated and saved before i enter any data. i use transition_post_status and add this code to my function:

if($old_status != 'publish' && $new_status == 'publish' && 
!empty($post->ID) && in_array( $post->post_type, array( 'product') )){
code...}

the result was the same as save_post_product. i use publish_product action and result didn't changed. i use draft_to_publish hook. it seems to fires after i enter product name and description. name sent to web-service but price and weight didn't. sku did not saved to database(why??). i know that there is another question here that claim save_post fires after post published and save to DB. but i think woocommerce is different. it seems save post after entering name and description and before entering price and etc. any suggestion???

1
save post after entering name and description and before entering price and etc Those fields are probably saved in postmeta so you would want a hook that fires after saving the post metadata (which may be hard because plugins often use post metadata). For example look in posts table in PHPmyAdmin and you wont find a column named price or weight. So to be able to store that data it's common to user the meta table (that is what it is there for). The meta tables are like key => value storage instead of a "normal" table.ArtisticPhoenix

1 Answers

2
votes

The action(s) you want are these:

add_action('woocommerce_update_product', 'productPublished');

add_action('woocommerce_new_product', 'productPublished');

function productPublished($product_id){
    //...
}

You can find them both (where they are triggered from) in the Woo source code here:

https://docs.woocommerce.com/wc-apidocs/source-class-WC_Product_Data_Store_CPT.html#237

I actually looked them up backwards by first finding where the source code for saving products was, and then looked for hooks in those methods (create/update).

 //found on Line 134 method create
 do_action( 'woocommerce_new_product', $id );


 //found on Line 237 method update
 do_action( 'woocommerce_update_product', $product->get_id() );

You'll also have to change this line:

function productPublished ($ID , $post , $update){
    $product = wc_get_product( $post->ID);
}

To

function productPublished($product_id){
    $product = wc_get_product( $product_id);
   //....
}

I don't think the other arguments (that are missing) matter to your code. Such as if it's an update or a new product, I also don't see $post used except to get the product ID, which we already have.

UPDATE (determine args for callback)

If you are not sure about the callback's arguments, you can look in the source code (as I did above) Or if you can find it in documentation Or as a last resort you can simply output them. The best way to output them is one of these 3

Here is an example I built with a minimally/simplified working hook system modeled after WordPress's. For testing reasons and because it's really not that hard to build when you know how it works:

//global storage (functions, not classes)
global $actions;
$actions = [];

//substitute wordpress add_action function (for testing only) 
function add_action($action, $callback, $priorty=10, $num_args=1){
    global $actions;
    $actions[$action][] = [
         'exec' => $callback,
         'priorty'=>$priorty,
         'num_args' => $num_args
    ];

}
//substitute wordpress do_action function (for testing only) 
function do_action($action, ...$args){
    // PHP5.6+ variadic (...$args) wraps all following arguments in an array inside $args (sort of the opposite of func_get_args)
    global $actions;

    if(empty($actions[$action])) return;
    //sort by priory
    usort($actions[$action], function($a,$b){
       //PHP7+ "spaceship" comparison operator (<=>)
       return $b['priorty']<=>$a['priorty'];
    });

    foreach($actions[$action] as $settings){
        //reduce the argument list
        call_user_func_array($settings['exec'], array_slice($args, 0, $settings['num_args']));
    }
}

//test callback
function callback1(){
     echo "\n\n".__FUNCTION__."\n";
    print_r(func_get_args());
}

//test callback
function callback2(){
    echo "\n\n".__FUNCTION__."\n";
    try{
        //throw an empty exception
        throw new Exception;
    }catch(\Exception $e){
         //pre tags preserve whitespace (white-space : pre)
        echo "<pre>";
        //output the stacktrace of the callback
        echo $e->getTraceAsString();
        echo "\n\n</pre>";
   }
}

//Register Hook callbacks, added in opposite order, sorted by priority
add_action('someaction', 'callback2', 5, 4);
add_action('someaction', 'callback1', 1, 5);

//execute hook
do_action('someaction', 1234, 'foo', ['one'=>1], new stdClass, false);

Output:

callback2
<pre>#0 [...][...](25): callback2(1234, 'foo', Array, Object(stdClass))
#1 [...][...](52): do_action('someaction', 1234, 'foo', Array, Object(stdClass), false)
#2 {main}

</pre>

callback1
Array
(
    [0] => 1234
    [1] => foo
    [2] => Array
        (
            [one] => 1
        )

    [3] => stdClass Object
        (
        )
    [4] =>
)

Sandbox

Stacktrace As you can see in the first output, we have a complete stacktrace of the application (minus the redacted info), including the function calls and the arguments used for those calls. Also note in this example, I registered it 2nd but the priority (set in add_action) made it execute first. Lastly, only 4 of the 5 arguments were used (also do to how add_action was called).

So do_action was called like this (with 'action' and 5 other args):

 do_action('someaction', 1234, 'foo', Array, Object(stdClass), false)

And the actual callback was called like this (without 'action' and only 4 other args):

 callback2(1234, 'foo', Array, Object(stdClass))

Function Arguments This is a bit more strait forward, but it doesn't give you the original call so you won't know the maximum number of args (which you can get from the call to do_action in the stacktrace). But if you just want a quick peek at the incoming data, it's perfect. Also I should mention this one uses all 5 args, which you can clearly see in the array for the second output. The [4] => is false, this is typically how print_r displays false (as just empty).

Debug Backtrace Unfortunately, debug_print_backtrace is disabled (for security reasons) in the sandbox, and Exception stacktrace is heavily redacted (typically it has file names and lines where functions are called from and located at) also for security reasons. Both of these can return arguments from things like connecting to the Database, which would contain the DB password in plain text, just for example. Anyway, debug_print_backtrace is pretty close to what an exception stack trace looks like anyway.

Summery

But in any case, this should give you an idea of what the data looks like. We can use functions like this (and Reflection) to interrogate the application at run time. I am sure there are other/more ways to do similar things too.

PS. It should go without saying, but I will say it anyway, these methods above work with any PHP function, and can be quite useful. Also as noted above, you should never show stacktraces on a live production machine.

Anyway, good luck.