woocommerce_wpml = $woocommerce_wpml; $this->sitepress = $sitepress; $this->post_translations = $post_translations; $this->wpdb = $wpdb; $this->wpml_cache = $wpml_cache ?: new WPML_WP_Cache( 'WCML_Products' ); } public function add_hooks() { if ( is_admin() ) { if ( ! isStandAlone() ) { add_filter( 'woocommerce_json_search_found_products', [ $this, 'filter_wc_searched_products_on_admin' ] ); add_action( 'wp_ajax_wpml_switch_post_language', [ $this, 'switch_product_variations_language' ], 9 ); add_filter( 'post_row_actions', [ $this, 'filter_product_actions' ], 10, 2 ); add_filter( 'woocommerce_product_type_query', [ $this, 'override_product_type_query' ], 10, 2 ); } } else { add_filter( 'woocommerce_related_products_args', [ $this, 'filter_related_products_args' ] ); if ( ! isStandAlone() ) { add_filter( 'woocommerce_json_search_found_products', [ $this, 'filter_wc_searched_products_on_front' ] ); add_filter( 'woocommerce_product_related_posts_query', [ $this, 'filter_related_products_query' ] ); add_filter( 'woocommerce_shortcode_products_query', [ $this, 'add_lang_to_shortcode_products_query' ] ); add_filter( 'woocommerce_product_file_download_path', [ $this, 'filter_file_download_path' ] ); add_filter( 'woocommerce_product_add_to_cart_url', [ $this, 'maybe_add_language_parameter' ] ); } } if ( ! isStandAlone() ) { add_filter( 'woocommerce_upsell_crosssell_search_products', [ $this, 'filter_woocommerce_upsell_crosssell_posts_by_language' ] ); add_action( 'woocommerce_after_product_ordering', [ $this, 'update_all_products_translations_ordering' ] ); add_filter( 'wpml_copy_from_original_custom_fields', [ $this, 'filter_excerpt_field_content_copy' ] ); add_filter( 'wpml_override_is_translator', [ $this, 'wcml_override_is_translator' ], 10, 3 ); add_filter( 'wpml_user_can_translate', [ $this, 'wcml_user_can_translate' ], 10, 2 ); add_filter( 'wc_product_has_unique_sku', [ $this, 'check_product_sku' ], 10, 3 ); add_filter( 'get_product_search_form', [ $this->sitepress, 'get_search_form_filter' ] ); add_filter( 'woocommerce_pre_customer_bought_product', Fns::withoutRecursion( Fns::identity(), [ $this, 'is_customer_bought_product' ] ), 10, 4 ); } add_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], 10, 3 ); add_filter( 'woocommerce_can_reduce_order_stock', [ $this, 'remove_post_meta_data_filter_on_checkout_stock_update' ] ); } /** * @param int|string $product_id * * @return bool */ public function is_original_product( $product_id ) { return ! $this->post_translations || null === $this->post_translations->get_source_lang_code( $product_id ); } /** * @param int|string $product_id * * @return null|string */ public function get_original_product_language( $product_id ) { return $this->post_translations ? $this->post_translations->get_element_lang_code( $this->get_original_product_id( $product_id ) ) : $this->sitepress->get_default_language(); } /** * @param int|string $product_id * * @return int|string */ public function get_original_product_id( $product_id ) { $original_product_id = $this->post_translations ? $this->post_translations->get_original_element( $product_id ) : null; return $original_product_id ?: $product_id; } public function is_variable_product( $product_id ) { $cache_key = $product_id; $cache_group = 'is_variable_product'; $temp_is_variable = wp_cache_get( $cache_key, $cache_group ); if ( $temp_is_variable ) { return $temp_is_variable; } $get_variation_term_taxonomy_ids = $this->wpdb->get_col( "SELECT tt.term_taxonomy_id FROM {$this->wpdb->terms} AS t LEFT JOIN {$this->wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE t.name = 'variable' AND tt.taxonomy = 'product_type'" ); $get_variation_term_taxonomy_ids = apply_filters( 'wcml_variation_term_taxonomy_ids', (array) $get_variation_term_taxonomy_ids ); $is_variable_product = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT count(object_id) FROM {$this->wpdb->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN (" . DB::prepareIn( $get_variation_term_taxonomy_ids, '%d' ) . ')', $product_id ) ); $is_variable_product = apply_filters( 'wcml_is_variable_product', $is_variable_product, $product_id ); wp_cache_set( $cache_key, $is_variable_product, $cache_group ); return $is_variable_product; } public function is_downloadable_product( $product ) { $product_id = $product->get_id(); $cache_key = 'is_downloadable_product_' . $product_id; $found = false; $is_downloadable = $this->wpml_cache->get( $cache_key, $found ); if ( ! $found ) { if ( $product->is_downloadable() ) { $is_downloadable = true; } elseif ( $this->is_variable_product( $product_id ) ) { foreach ( $product->get_available_variations() as $variation ) { if ( $variation['is_downloadable'] ) { $is_downloadable = true; break; } } } $this->wpml_cache->set( $cache_key, $is_downloadable ); } return $is_downloadable; } public function is_grouped_product( $product_id ) { $get_variation_term_taxonomy_id = $this->wpdb->get_var( "SELECT tt.term_taxonomy_id FROM {$this->wpdb->terms} AS t LEFT JOIN {$this->wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE t.name = 'grouped'" ); $is_grouped_product = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT count(object_id) FROM {$this->wpdb->term_relationships} WHERE object_id = %d AND term_taxonomy_id = %d ", $product_id, $get_variation_term_taxonomy_id ) ); return $is_grouped_product; } public function get_translation_flags( $active_languages, $slang = false, $job_language = false ) { $available_languages = []; foreach ( $active_languages as $key => $language ) { if ( $job_language && $language['code'] != $job_language ) { continue; } elseif ( ! $slang || ( ( $slang != $language['code'] ) && ( current_user_can( 'wpml_operate_woocommerce_multilingual' ) || wpml_check_user_is_translator( $slang, $language['code'] ) ) && ( ! isset( $_POST['translation_status_lang'] ) || ( isset( $_POST['translation_status_lang'] ) && ( $_POST['translation_status_lang'] == $language['code'] ) || $_POST['translation_status_lang'] == '' ) ) ) ) { $available_languages[ $key ]['name'] = $language['english_name']; $available_languages[ $key ]['flag_url'] = $this->sitepress->get_flag_url( $language['code'] ); } } return $available_languages; } public function get_translation_statuses( $original_product_id, $product_translations, $active_languages, $slang = false, $trid = false, $job_language = false ) { $status_display_factory = new WPML_Post_Status_Display_Factory( $this->sitepress ); $status_display = $status_display_factory->create(); foreach ( $active_languages as $language ) { if ( $job_language && $language['code'] != $job_language ) { continue; } elseif ( isset( $product_translations[ $language['code'] ] ) && $product_translations[ $language['code'] ]->original ) { ?> wpdb->get_var( $this->wpdb->prepare( "SELECT MAX(tj.job_id) FROM {$this->wpdb->prefix}icl_translate_job AS tj LEFT JOIN {$this->wpdb->prefix}icl_translation_status AS ts ON tj.rid = ts.rid WHERE ts.translation_id=%d", $product_translations[ $language['code'] ]->translation_id ) ); } else { $job_id = false; } if ( ! current_user_can( 'wpml_manage_woocommerce_multilingual' ) && isset( $product_translations[ $language['code'] ] ) ) { /** @var stdClass */ $tr_status = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT status,translator_id FROM {$this->wpdb->prefix}icl_translation_status WHERE translation_id = %d", $product_translations[ $language['code'] ]->translation_id ) ); if ( ! is_null( $tr_status ) && get_current_user_id() != $tr_status->translator_id ) { if ( $tr_status->status == ICL_TM_IN_PROGRESS ) { ?> status == ICL_TM_WAITING_FOR_TRANSLATOR && ! $job_id ) { $tr_job_id = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT j.job_id FROM {$this->wpdb->prefix}icl_translate_job j JOIN {$this->wpdb->prefix}icl_translation_status s ON j.rid = s.rid WHERE s.translation_id = %d", $product_translations[ $language['code'] ]->translation_id ) ); $translation_queue_page = admin_url( 'admin.php?page=' . WPML_TM_FOLDER . '/menu/translations-queue.php&job_id=' . $tr_job_id ); $edit_url = apply_filters( 'icl_job_edit_url', $translation_queue_page, $tr_job_id ); ?> get_status_html( $original_product_id, $language['code'] ); } } } /** * @param bool $is_translator * @param int $user_id * @param array $args * * @return bool */ public function wcml_override_is_translator( $is_translator, $user_id, $args ) { if ( current_user_can( 'wpml_operate_woocommerce_multilingual' ) ) { if ( ! ( isset( $args['post_id'] ) && $args['post_id'] ) ) { return true; } $wc_post_types = [ 'product', 'product_variation', 'shop_coupon', 'shop_order', 'shop_order_refund' ]; $post_type = get_post_type( $args['post_id'] ); if ( in_array( $post_type, $wc_post_types, true ) ) { return true; } } return $is_translator; } /** * @param bool $user_can_translate * @param WP_User $user * * @return bool */ public function wcml_user_can_translate( $user_can_translate, $user ) { if ( user_can( $user, 'wpml_operate_woocommerce_multilingual' ) ) { return true; } return $user_can_translate; } // product quickedit. public function filter_product_actions( $actions, $post ) { if ( $post->post_type == 'product' && ! $this->is_original_product( $post->ID ) && isset( $actions['inline hide-if-no-js'] ) ) { $new_actions = []; foreach ( $actions as $key => $action ) { if ( $key == 'inline hide-if-no-js' ) { $new_actions['quick_hide'] = '' . __( 'Quick Edit', 'woocommerce-multilingual' ) . ''; } else { $new_actions[ $key ] = $action; } } return $new_actions; } return $actions; } /** * Takes off translated products from the Up-sells/Cross-sells tab. */ public function filter_woocommerce_upsell_crosssell_posts_by_language( $posts ) { foreach ( $posts as $key => $post ) { $post_id = $posts[ $key ]->ID; $post_data = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->prefix}icl_translations WHERE element_id = %d ", $post_id ), ARRAY_A ); if ( $post_data['language_code'] !== $this->sitepress->get_current_language() ) { unset( $posts[ $key ] ); } } return $posts; } /** * Filters products by language * * @param array $found_products * @param bool $language * * @return array */ private function filter_found_products_by_language( $found_products, $language = false ) { if ( ! $language ) { $language = $this->sitepress->get_current_language(); } foreach ( $found_products as $product_id => $product_name ) { if ( $this->post_translations->get_element_lang_code( $product_id ) !== $language ) { unset( $found_products[ $product_id ] ); } } return $found_products; } /** * @param array $found_products * * @return array */ public function filter_wc_searched_products_on_front( $found_products ) { return $this->filter_found_products_by_language( $found_products ); } /** * @param array $found_products * * @return array */ public function filter_wc_searched_products_on_admin( $found_products ) { if ( isset( $_COOKIE['_wcml_dashboard_order_language'] ) ) { $found_products = $this->filter_found_products_by_language( $found_products, $_COOKIE['_wcml_dashboard_order_language'] ); } else { $found_products = $this->filter_found_products_by_language( $found_products ); } return $found_products; } /** * Sync to translations after sorting original products. */ public function update_all_products_translations_ordering() { if ( $this->woocommerce_wpml->settings['products_sync_order'] ) { $current_language = $this->sitepress->get_current_language(); $default_language = $this->sitepress->get_default_language(); if ( $current_language === $default_language ) { $this->wpdb->query( $this->wpdb->prepare( " UPDATE {$this->wpdb->posts} posts JOIN {$this->wpdb->prefix}icl_translations trans ON posts.ID = trans.element_id AND trans.element_type = 'post_product' SET posts.menu_order = ( SELECT menu_order FROM {$this->wpdb->posts} JOIN {$this->wpdb->prefix}icl_translations ON ID = element_id AND element_type = 'post_product' WHERE language_code = '%s' AND trid = trans.trid ) WHERE trans.language_code <> %s ", $default_language, $default_language ) ); } } } /** * Update menu_order fro translations after ordering original products * * @param int $product_id */ public function update_order_for_product_translations( $product_id ) { if ( isset( $this->woocommerce_wpml->settings['products_sync_order'] ) && $this->woocommerce_wpml->settings['products_sync_order'] ) { $current_language = $this->sitepress->get_current_language(); if ( $current_language == $this->sitepress->get_default_language() ) { $translations = array_diff( $this->post_translations->get_element_translations( $product_id ), [ $product_id ] ); if ( $translations ) { $menu_order = $this->wpdb->get_var( $this->wpdb->prepare( "SELECT menu_order FROM {$this->wpdb->posts} WHERE ID = %d", $product_id ) ); $this->wpdb->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->posts} SET menu_order = %d WHERE ID IN (" . wpml_prepare_in( $translations, '%d' ) . ')', $menu_order ) ); } } } } public function filter_excerpt_field_content_copy( $elements ) { if ( $elements['post_type'] == 'product' ) { $elements['excerpt'] ['editor_type'] = 'editor'; } if ( function_exists( 'format_for_editor' ) ) { // WordPress 4.3 uses format_for_editor $elements['excerpt']['value'] = htmlspecialchars_decode( format_for_editor( $elements['excerpt']['value'], $_POST['excerpt_type'] ) ); } else { // Backwards compatible for WordPress < 4.3 if ( $_POST['excerpt_type'] == 'rich' ) { $elements['excerpt']['value'] = htmlspecialchars_decode( wp_richedit_pre( $elements['excerpt']['value'] ) ); } else { $elements['excerpt']['value'] = htmlspecialchars_decode( wp_htmledit_pre( $elements['excerpt']['value'] ) ); } } return $elements; } public function filter_related_products_args( $args ) { if ( $this->woocommerce_wpml->settings['enable_multi_currency'] == WCML_MULTI_CURRENCIES_INDEPENDENT && isset( $this->woocommerce_wpml->settings['display_custom_prices'] ) && $this->woocommerce_wpml->settings['display_custom_prices'] ) { $client_currency = $this->woocommerce_wpml->multi_currency->get_client_currency(); $woocommerce_currency = wcml_get_woocommerce_currency_option(); if ( $client_currency != $woocommerce_currency ) { $args['meta_query'][] = [ 'key' => '_wcml_custom_prices_status', 'value' => 1, 'compare' => '=', ]; } } return $args; } /** * @param array $query * * @return array */ public function filter_related_products_query( $query ) { $query['join'] .= " LEFT JOIN {$this->wpdb->prefix}icl_translations AS icl ON icl.element_id = p.ID "; $query['where'] .= $this->wpdb->prepare( ' AND icl.language_code = %s ', $this->sitepress->get_current_language() ); return $query; } /* * get meta ids for multiple values post meta key */ public function get_mid_ids_by_key( $post_id, $meta_key ) { $ids = $this->wpdb->get_col( $this->wpdb->prepare( "SELECT meta_id FROM {$this->wpdb->postmeta} WHERE post_id = %d AND meta_key = %s", $post_id, $meta_key ) ); if ( $ids ) { return $ids; } return false; } // count "in progress" and "waiting on translation" as untranslated too public function get_untranslated_products_count( $language ) { $count = 0; $products = $this->wpdb->get_results( " SELECT p.ID, t.trid, t.language_code FROM {$this->wpdb->posts} AS p LEFT JOIN {$this->wpdb->prefix}icl_translations AS t ON t.element_id = p.id WHERE p.post_type = 'product' AND t.element_type = 'post_product' AND t.source_language_code IS NULL " ); foreach ( $products as $product ) { if ( $product->language_code == $language ) { continue; } $translation_status = apply_filters( 'wpml_translation_status', null, $product->trid, $language ); if ( in_array( $translation_status, [ ICL_TM_NOT_TRANSLATED, ICL_TM_WAITING_FOR_TRANSLATOR, ICL_TM_IN_PROGRESS ] ) ) { $count++; } } return $count; } public function is_hide_resign_button() { global $iclTranslationManagement; $hide_resign = false; if ( isset( $_GET['source_language_code'] ) && isset( $_GET['language_code'] ) ) { $from_lang = $_GET['source_language_code']; $to_lang = $_GET['language_code']; } elseif ( isset( $_GET['job_id'] ) ) { $job = $iclTranslationManagement->get_translation_job( $_GET['job_id'] ); if ( $job ) { $from_lang = $job->source_language_code; $to_lang = $job->language_code; } } if ( isset( $from_lang, $to_lang ) ) { $translators = $iclTranslationManagement->get_blog_translators( [ 'from' => $from_lang, 'to' => $to_lang, ] ); if ( empty( $translators ) || ( sizeof( $translators ) == 1 && $translators[0]->ID == get_current_user_id() ) ) { $hide_resign = true; } } return $hide_resign; } public function switch_product_variations_language() { if ( ! isset( $_POST['nonce'] ) ) { return; } if ( ! wp_verify_nonce( $_POST['nonce'], \WPML_Post_Edit_Ajax::AJAX_ACTION_SWITCH_POST_LANGUAGE ) ) {// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized return; } if ( ! current_user_can( 'edit_products' ) ) { return; } $lang_to = false; $post_id = false; if ( isset( $_POST['wpml_to'] ) ) { $lang_to = $_POST['wpml_to']; } if ( isset( $_POST['wpml_post_id'] ) ) { $post_id = $_POST['wpml_post_id']; } if ( $post_id && $lang_to && get_post_type( $post_id ) == 'product' ) { $product_variations = $this->woocommerce_wpml->sync_variations_data->get_product_variations( $post_id ); foreach ( $product_variations as $product_variation ) { $trid = $this->sitepress->get_element_trid( $product_variation->ID, 'post_product_variation' ); $current_prod_variation_id = apply_filters( 'translate_object_id', $product_variation->ID, 'product_variation', false, $lang_to ); if ( is_null( $current_prod_variation_id ) ) { $this->sitepress->set_element_language_details( $product_variation->ID, 'post_product_variation', $trid, $lang_to ); foreach ( get_post_custom( $product_variation->ID ) as $meta_key => $meta ) { foreach ( $meta as $meta_value ) { if ( substr( $meta_key, 0, 10 ) == 'attribute_' ) { $trn_post_meta = $this->woocommerce_wpml->attributes->get_translated_variation_attribute_post_meta( $meta_value, $meta_key, $product_variation->ID, $product_variation->ID, $lang_to ); update_post_meta( $product_variation->ID, $trn_post_meta['meta_key'], $trn_post_meta['meta_value'] ); } } } } } } } public function check_product_sku( $sku_found, $product_id, $sku ) { if ( $sku_found ) { $product_trid = $this->post_translations->get_element_trid( $product_id ); $products = $this->wpdb->get_results( $this->wpdb->prepare( " SELECT p.ID FROM {$this->wpdb->posts} as p LEFT JOIN {$this->wpdb->postmeta} as pm ON ( p.ID = pm.post_id ) WHERE p.post_type IN ( 'product', 'product_variation' ) AND p.post_status = 'publish' AND pm.meta_key = '_sku' AND pm.meta_value = '%s' ", wp_slash( $sku ) ) ); $sku_found = false; foreach ( (array) $products as $product ) { $trid = $this->post_translations->get_element_trid( $product->ID ); if ( $product_trid !== $trid ) { $sku_found = true; break; } } } return $sku_found; } public function add_lang_to_shortcode_products_query( $query_args ) { $query_args['lang'] = $this->sitepress->get_current_language(); return $query_args; } /** * Get file download path in correct domain * * @param string $file_path file path URL * @return string */ public function filter_file_download_path( $file_path ) { $is_per_domain = $this->sitepress->get_wp_api()->constant( 'WPML_LANGUAGE_NEGOTIATION_TYPE_DOMAIN' ) === (int) $this->sitepress->get_setting( 'language_negotiation_type' ); if ( $is_per_domain ) { $wpml_url_helper = new WPML_URL_Converter_Url_Helper(); if ( strpos( trim( $file_path ), $wpml_url_helper->get_abs_home() ) === 0 ) { $file_path = $this->sitepress->convert_url( $file_path ); } } return $file_path; } /** * * @return bool */ public function is_product_display_as_translated_post_type() { return apply_filters( 'wpml_is_display_as_translated_post_type', false, 'product' ); } /** * @param bool $value * @param string $customer_email * @param int $user_id * @param int $product_id * * @return bool */ public function is_customer_bought_product( $value, $customer_email, $user_id, $product_id ) { if ( $value ) { return $value; } $post_type = get_post_type( $product_id ); $trid = apply_filters( 'wpml_element_trid', 0, $product_id, 'post_' . $post_type ); // $has_bought_original_or_translation :: object -> bool $has_bought_original_or_translation = pipe( Obj::prop( 'element_id' ), partial( 'wc_customer_bought_product', $customer_email, $user_id ) ); return (bool) wpml_collect( apply_filters( 'wpml_get_element_translations', [], $trid, $post_type ) )->first( $has_bought_original_or_translation ); } public function filter_product_data( $data, $product_id, $meta_key ) { if ( ! $meta_key ) { $post_type = get_post_type( $product_id ); if ( in_array( $post_type, [ 'product', 'product_variation' ], true ) ) { remove_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], 10 ); $data = get_post_meta( $product_id ); $meta_keys_to_filter = []; $is_mc_enabled = (int) $this->woocommerce_wpml->settings['enable_multi_currency'] === (int) $this->sitepress->get_wp_api()->constant( 'WCML_MULTI_CURRENCIES_INDEPENDENT' ); if ( $is_mc_enabled ) { $meta_keys_to_filter = wcml_price_custom_fields( $product_id ); } if ( ! is_admin() && ! isStandAlone() ) { if ( 'product' === $post_type ) { $meta_keys_to_filter[] = WCML_Comments::WC_RATING_COUNT_KEY; $meta_keys_to_filter[] = WCML_Comments::WC_REVIEW_COUNT_KEY; $meta_keys_to_filter[] = WCML_Comments::WC_AVERAGE_RATING_KEY; } $is_original_product = $this->woocommerce_wpml->products->is_original_product( $product_id ); if ( ! $is_original_product ) { $meta_keys_to_filter[] = '_thumbnail_id'; if ( 'product' === $post_type && is_single() ) { $meta_keys_to_filter[] = '_product_image_gallery'; } } } foreach ( $meta_keys_to_filter as $meta_key ) { $data[ $meta_key ][0] = get_post_meta( $product_id, $meta_key, true ); } add_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], 10, 3 ); } } return $data; } /** * @param int $product_id * * @return null|string */ public function get_product_price_from_db( $product_id ) { return $this->wpdb->get_var( $this->wpdb->prepare( "SELECT meta_value FROM {$this->wpdb->postmeta} WHERE `meta_key` = '_price' AND post_id = %d ", $product_id ) ); } /** * return not cached value for product * * @param bool $product_type * @param int $product_id * * @return bool|string */ public function override_product_type_query( $product_type, $product_id ) { if ( 'product' === get_post_type( $product_id ) ) { $product_type = 'simple'; $terms = get_the_terms( $product_id, 'product_type' ); if ( $terms ) { $product_type = sanitize_title( current( $terms )->name ); } } return $product_type; } /** * @param bool $reduce_stock * * @return bool */ public function remove_post_meta_data_filter_on_checkout_stock_update( $reduce_stock ) { if ( isset( $_GET['wc-ajax'] ) && 'checkout' === $_GET['wc-ajax'] ) { remove_filter( 'get_post_metadata', [ $this, 'filter_product_data' ], 10 ); } return $reduce_stock; } public function maybe_add_language_parameter( $url ) { if ( 'no' === get_option( 'woocommerce_enable_ajax_add_to_cart' ) && constant( 'WPML_LANGUAGE_NEGOTIATION_TYPE_PARAMETER' ) === (int) $this->sitepress->get_setting( 'language_negotiation_type' ) ) { $current_language = $this->sitepress->get_current_language(); if ( $current_language !== $this->sitepress->get_default_language() ) { $url .= '&lang=' . $this->sitepress->get_current_language(); } } return $url; } /** * @param int $product_id * @param string $status */ public function update_stock_status( $product_id, $status ) { update_post_meta( $product_id, '_stock_status', $status ); $this->wpdb->query( $this->wpdb->prepare( "UPDATE {$this->wpdb->wc_product_meta_lookup} SET stock_status = %s WHERE product_id = %d", $status, $product_id ) ); } }