WooCommerce update a product when opening single product view - wordpress

I have to update my product with data that comes from an API query. I would like to update the products data when visiting the single products view. I'm new to WordPress and WooCommerce, but I think this can be done through hooks?
I've tried looking for hooks but haven't had any luck yet

You could do something like the example below.
This will run every time the product is viewed. Of course, you have to add your own API call and adapt the function to your data structure.
add_action('wp', 'update_product_data');
function update_product_data(){
if (class_exists('WooCommerce') && is_product()) {
$product = wc_get_product( get_the_ID() );
$product_id = $product->get_id();
// Call the external API to get the updated product data
$api_url = 'http://example.com/api/product/' . $product_id;
$response = wp_remote_get($api_url);
// Check for successful response and decode the JSON data
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) == 200) {
$data = json_decode(wp_remote_retrieve_body($response), true);
// Update the product's weight and description with the data from the API
$product->set_weight($data['weight']);
$product->set_description($data['description']);
$product->save();
}
}
}

Related

Wordpress admin: Get current open post (order) edit page meta value

We have a website running on woocommerce and we run a custom tax solution there. On frontend everything works perfect atleast in admin as we have set, that tax is calculated by store address, we get the basic store country into all of the orders. What we are trying to achieve is to replace this country in admin from current open order post meta. But we are unlucky and we are not getting any data. Is there any possiblity to get current open post meta value?
function custom_base_country($base_country ){
if (is_admin()){
global $woocommerce;
$order = $_GET['post'];
$country = get_post_meta($order->id, 'country_code', true);
$base_country = $country;
}
return $base_country;
}
add_filter('woocommerce_countries_base_country', 'custom_base_country', 10, 1);
Found out the answer by changing the code to $country = get_post_meta($order, 'country_code', true);

Is there a hook that precedes Woocommerce API's duplicate SKU check?

I have a site with Woocommerce and WPML + Multilingual Woocommerce installed. My problem is that I try to insert a product as a translation of a previously entered product without being aware of the ID of the main product. If I enter the ID as translation_of it works; both products share the same SKU and the translation has the SKU field disabled, which is how I want it to work. But I don't want to enter translation_of into the data that gets sent to Woocommerce. I want to only use the SKU and then let Wordpress first check if a product with that SKU already exists and replace sku with translation_of if it does.
This is how I went about it:
add_filter('woocommerce_api_create_product_data', '__create_product_data', -100, 2);
function __create_product_data($data, $api) {
if(isset($data['sku']) && $product_id = wc_get_product_id_by_sku($data['sku'])) {
$product_id = apply_filters('wpml_object_id', $product_id, 'product');
$data['translation_of'] = $product_id;
unset($data['sku']);
}
return $data;
}
But it seems to me that execution arrives at this point long after the SKU has been checked, because I noticed that I can return nothing and I still get product_invalid_sku error back. What would be the correct hook or does such a hook even exist?
My own solution:
add_filter('rest_pre_dispatch', '__rest_pre_dispatch', 10, 3);
function __rest_pre_dispatch($result, $server, $request) {
$sku = $request->get_param('sku');
if ($sku) {
$id = wc_get_product_id_by_sku($sku);
if ($id) {
$product_id = apply_filters('wpml_object_id', $id, 'product');
$request->set_param('translation_of', $product_id);
$request->offsetUnset('sku');
}
}
return $result;
}

WooCommerce product update – check if a field's value has changed

I'm using the following code to hook a product update in woocommerce:
add_action('woocommerce_update_product', 'on_update_product', 10, 2);
function on_update_product($product_id, $product){
// code here
}
Is there a way to check if certain fields have changed, compared to the previously stored version of the product?
Thanks!
The best way to do this that I know is with hashes.
add_action('woocommerce_update_product', 'on_update_product', 10, 2);
function on_update_product($product_id, $product){
//create a hash from data you want to track
$hash = md5(json_encode([
$product->get_name(),
$product->get_price(),
"etc....."
]));
//get the hash before the product update
$hashBefore = get_post_meta( $product_id, "hashKey", true );
//check if de hash is diffrend
if ($hash !== $hashBefore) {
// Store the new hash
add_post_meta($product_id, "hashKey", $hash);
// exicute your code
// .....
}
// you can duplicate this process if you want to track individual fields
$hash2 = md5(json_encode([
$product->get_sku(),
]));
$hashBefore2 = get_post_meta( $product_id, "hashKey2", true );
if ($hash2 !== $hashBefore2) {
add_post_meta($product_id, "hashKey2", $hash2);
}
}
To get data out of the product object check this resource:
https://businessbloomer.com/woocommerce-easily-get-product-info-title-sku-desc-product-object/
I hope this suits your situation
I would recommend hooking to another action. I use it to identify changes in orders, but it actually can use for any woocomercce related object types (orders, products, coupons, subscriptions etc.)
woocommerce_before_[objectName]_object_save
for your purpose you can use:
add_action('woocommerce_before_product_object_save', 'identify_product_change', 100, 2);
function identify_product_change($product, $data){
$posted_info = $_POST; // Use this to get the new information
$price = $product->get_price(); //Example of getting the "old" product information
}
Having that said, you need to be careful, since this hook may be initiated from different triggers (some background processes etc). You may want to have some caution measurements:
use $_POST['action'] == 'editpost' to make sure the action is an
actual "Update" click from the admin edit page.
use (is_admin()) to limit it only to admin area
you can use (!defined('DOING_CRON')) to make sure it won't run on any cron execution
and you can use (!defined('DOING_AJAX')) to make sure it won't run on ajax calls
this way you can limit it only to the exact action you wish to catch.

Adding a Woocommerce Downloadable File From Custom Field

Using Advanced Custom Fields, I've added a "Post Object" field to my Woocommerce edit page, set to return Post IDs.
I'm trying to get data from these posts, and automatically populate my Woocommerce Downloadable Files from them.
The problem with my current code is that if I change the data in the custom field, and then save my product, it doesn't update the downloads. I need to update the product again for the downloads to be affected.
add_action( 'woocommerce_admin_process_product_object', 'mysite_add_downloads', 50, 1 );
function mysite_add_downloads( $product ){
$mysite_select_albums = get_field('mysite_select_albums');
$downloads = array(); // Clear $downloads variable
foreach ($mysite_select_albums as $album) {
// Gets Information About Download From Album Post Type
$post = get_post($album);
$file_title = $post->post_title;
$file_url = "http://changingthinstosimplify.com/";
$file_md5 = md5($file_url);
$download = new WC_Product_Download(); // Get an instance of the WC_Product_Download Object
// Set the download data
$download->set_name($file_title);
$download->set_id($file_md5);
$download->set_file($file_url);
$downloads[$file_md5] = $download; // Insert the new download to the array of downloads
}
$product->set_downloads($downloads); // Set new array of downloads
}

woocommerce_output_related_products_args hook not working as expected

I want to show a specific product with id 64391 in related products section but the following code doesn't work for that. Am I missing something?
add_filter( 'woocommerce_output_related_products_args','msrp_reconfigure_related_products_args', 20 );
public function msrp_reconfigure_related_products_args($args)
{
$args['posts_per_page'] = 5;
$args['columns'] = 5;
$args['post__in'] = array(64391);
return $args;
}
As you can find on the WC core source. The filter hook woocommerce_output_related_products_args triggers via wc-template-functions.php file and passes it's return to woocommerce_related_products function. So the $args parameter isn't the args used in db query and you can not use post__in key in it.
Instead, to add specific products to related products section, you can use woocommerce_related_products filter as below:
add_filter('woocommerce_related_products', 'add_related_products');
function add_related_products($related_product_ids)
{
// WC source code stores IDs as string in this array, so I did that too
$related_product_ids[] = '81';
return $related_product_ids;
}
Tested and it's working.
Note: This filter hook has 3 parameters: $related_posts, $product_id and $args. You can limit these new products to only display on specific single product pages, by checking $product_id

Resources