WCML_Synchronize_Variations_Data

The WooCommerce Multilingual WCML Synchronize Variations Data class.

Defined (1)

The class is defined in the following location(s).

/inc/translation-editor/class-wcml-synchronize-variations-data.php  
  1. class WCML_Synchronize_Variations_Data{ 
  2.  
  3. private $woocommerce_wpml; 
  4. private $sitepress; 
  5. private $wpdb; 
  6.  
  7. public function __construct( &$woocommerce_wpml, &$sitepress, &$wpdb ) { 
  8. $this->woocommerce_wpml = $woocommerce_wpml; 
  9. $this->sitepress = $sitepress; 
  10. $this->wpdb = $wpdb; 
  11.  
  12. add_action( 'woocommerce_ajax_save_product_variations', array( $this, 'sync_product_variations_action' ), 11 ); 
  13. add_action( 'wp_ajax_woocommerce_remove_variations', array( $this, 'remove_translations_for_variations' ), 9 ); 
  14.  
  15. //save taxonomy in WPML interface 
  16. add_action( 'wp_ajax_wpml_tt_save_term_translation', array( $this, 'update_taxonomy_in_variations' ), 7 ); 
  17.  
  18. add_action( 'wp_ajax_woocommerce_remove_variation', array( $this, 'remove_variation_ajax' ), 9 ); 
  19.  
  20. public function sync_product_variations_action( $product_id ) { 
  21.  
  22. if( $this->woocommerce_wpml->products->is_original_product( $product_id ) ) { 
  23.  
  24. $this->sync_product_variations_custom_data( $product_id ); 
  25.  
  26. $trid = $this->sitepress->get_element_trid( $product_id, 'post_product' ); 
  27.  
  28. if ( empty( $trid ) ) { 
  29. $trid = $this->wpdb->get_var( 
  30. $this->wpdb->prepare( 
  31. "SELECT trid FROM {$this->wpdb->prefix}icl_translations 
  32. WHERE element_id = %d AND element_type = 'post_product'",  
  33. $product_id ) 
  34. ); 
  35. $translations = $this->sitepress->get_element_translations( $trid, 'post_product' ); 
  36. foreach ( $translations as $translation ) { 
  37. if ( !$translation->original ) { 
  38. $this->sync_product_variations($product_id, $translation->element_id, $translation->language_code); 
  39. $this->woocommerce_wpml->attributes->sync_default_product_attr( $product_id, $translation->element_id, $translation->language_code ); 
  40.  
  41. public function sync_product_variations_custom_data( $product_id ) { 
  42.  
  43. $is_variable_product = $this->woocommerce_wpml->products->is_variable_product( $product_id ); 
  44. if( $is_variable_product ) { 
  45. $get_all_post_variations = $this->wpdb->get_results( 
  46. $this->wpdb->prepare( 
  47. "SELECT * FROM {$this->wpdb->posts} 
  48. WHERE post_status IN ('publish', 'private') 
  49. AND post_type = 'product_variation' 
  50. AND post_parent = %d 
  51. ORDER BY ID" 
  52. , $product_id) 
  53. ); 
  54.  
  55. foreach ( $get_all_post_variations as $k => $post_data ) { 
  56.  
  57. if ( $this->woocommerce_wpml->settings[ 'enable_multi_currency' ] == WCML_MULTI_CURRENCIES_INDEPENDENT ) { 
  58. $this->woocommerce_wpml->multi_currency->custom_prices->sync_product_variations_custom_prices( $post_data->ID ); 
  59.  
  60. //save files option 
  61. $this->woocommerce_wpml->downloadable->save_files_option( $post_data->ID ); 
  62.  
  63.  
  64. /** 
  65. * sync product variations 
  66. * $product_id - original product id 
  67. * $tr_product_id - translated product id 
  68. * $lang - trnsl language 
  69. * $data - array of values (when we save original product this array is empty, but when we update translation in this array we have price values and etc.) * 
  70. * */ 
  71. public function sync_product_variations( $product_id, $tr_product_id, $lang, $data = false, $trbl = false ) { 
  72. global $wpml_post_translations; 
  73.  
  74. $is_variable_product = $this->woocommerce_wpml->products->is_variable_product( $product_id ); 
  75.  
  76. if( $is_variable_product ) { 
  77.  
  78. remove_action ( 'save_post', array( $wpml_post_translations, 'save_post_actions' ), 100, 2 ); 
  79.  
  80. $all_variations = $this->get_product_variations( $product_id ); 
  81. $current_variations = $this->get_product_variations( $tr_product_id ); 
  82.  
  83. foreach( $all_variations as $key => $post_data ) { 
  84. $original_variation_id = $post_data->ID; 
  85. //save files option 
  86. $this->woocommerce_wpml->downloadable->save_files_option( $original_variation_id ); 
  87.  
  88. $variation_id = $this->get_variation_id_by_lang( $lang, $original_variation_id ); 
  89.  
  90. if( !empty( $variation_id ) && !is_null( $variation_id ) ) { 
  91. //unset variation from array to delete variations that no longer exist 
  92. unset( $current_variations[ $key ] ); 
  93. // Update variation 
  94. wp_update_post( array( 
  95. 'ID' => $variation_id,  
  96. 'post_status' => $post_data->post_status,  
  97. 'post_modified' => $post_data->post_modified,  
  98. 'post_modified_gmt' => $post_data->post_modified_gmt,  
  99. 'post_parent' => $tr_product_id, // current post ID 
  100. 'menu_order' => $post_data->menu_order,  
  101. )); 
  102. } else { 
  103. // Add new variation 
  104. $guid = $post_data->guid; 
  105. $replaced_guid = str_replace( $product_id, $tr_product_id, $guid ); 
  106. $slug = $post_data->post_name; 
  107. $replaced_slug = str_replace( $product_id, $tr_product_id, $slug ); 
  108. $variation_id = wp_insert_post( array( 
  109. 'post_author' => $post_data->post_author,  
  110. 'post_date_gmt' => $post_data->post_date_gmt,  
  111. 'post_content' => $post_data->post_content,  
  112. 'post_title' => $post_data->post_title,  
  113. 'post_excerpt' => $post_data->post_excerpt,  
  114. 'post_status' => $post_data->post_status,  
  115. 'comment_status' => $post_data->comment_status,  
  116. 'ping_status' => $post_data->ping_status,  
  117. 'post_password' => $post_data->post_password,  
  118. 'post_name' => $replaced_slug,  
  119. 'to_ping' => $post_data->to_ping,  
  120. 'pinged' => $post_data->pinged,  
  121. 'post_modified' => $post_data->post_modified,  
  122. 'post_modified_gmt' => $post_data->post_modified_gmt,  
  123. 'post_content_filtered' => $post_data->post_content_filtered,  
  124. 'post_parent' => $tr_product_id, // current post ID 
  125. 'guid' => $replaced_guid,  
  126. 'menu_order' => $post_data->menu_order,  
  127. 'post_type' => $post_data->post_type,  
  128. 'post_mime_type' => $post_data->post_mime_type,  
  129. 'comment_count' => $post_data->comment_count 
  130. )); 
  131. add_post_meta( $variation_id, '_wcml_duplicate_of_variation', $original_variation_id ); 
  132. $trid = $this->sitepress->get_element_trid( $original_variation_id, 'post_product_variation' ); 
  133. $this->sitepress->set_element_language_details( $variation_id, 'post_product_variation', $trid, $lang ); 
  134.  
  135. //sync description 
  136. if( isset( $data[ md5( '_variation_description'.$original_variation_id ) ] ) ) { 
  137. update_post_meta( $variation_id, '_variation_description', $data[ md5( '_variation_description'.$original_variation_id ) ] ); 
  138.  
  139. //sync media 
  140. $this->woocommerce_wpml->media->sync_thumbnail_id( $original_variation_id, $variation_id, $lang ); 
  141. //sync file_paths 
  142. $this->woocommerce_wpml->downloadable->sync_files_to_translations( $original_variation_id, $variation_id, $data ); 
  143.  
  144. //sync taxonomies 
  145. $this->sync_variations_taxonomies( $original_variation_id, $variation_id, $lang ); 
  146.  
  147. $this->duplicate_variation_data( $original_variation_id, $variation_id, $data, $lang, $trbl ); 
  148.  
  149. $this->delete_removed_variation_attributes( $product_id, $variation_id ); 
  150.  
  151. //refresh parent-children transients 
  152. delete_transient( 'wc_product_children_' . $tr_product_id ); 
  153. delete_transient( '_transient_wc_product_children_ids_' . $tr_product_id ); 
  154.  
  155.  
  156. // Delete variations that no longer exist 
  157. foreach( $current_variations as $key => $current_post_variation ) { 
  158. wp_delete_post( $current_post_variation->ID, true ); 
  159.  
  160. add_action ( 'save_post', array( $wpml_post_translations, 'save_post_actions' ), 100, 2 ); 
  161.  
  162. public function get_variation_id_by_lang( $lang, $original_variation_id ) { 
  163. return $this->wpdb->get_var( 
  164. $this->wpdb->prepare( 
  165. "SELECT post_id FROM {$this->wpdb->postmeta} AS pm 
  166. JOIN {$this->wpdb->prefix}icl_translations AS tr ON tr.element_id = pm.post_id 
  167. WHERE tr.element_type = 'post_product_variation' 
  168. AND tr.language_code = %s 
  169. AND pm.meta_key = '_wcml_duplicate_of_variation' 
  170. AND pm.meta_value = %d",  
  171. $lang, $original_variation_id ) 
  172. ); 
  173.  
  174. public function sync_variations_taxonomies( $original_variation_id, $tr_variation_id, $lang ) { 
  175.  
  176. remove_filter( 'terms_clauses', array( $this->sitepress, 'terms_clauses' ), 10, 4 ); 
  177.  
  178. if( $this->woocommerce_wpml->sync_product_data->check_if_product_fields_sync_needed( $original_variation_id, $tr_variation_id, 'taxonomies' ) ) { 
  179.  
  180. $all_taxs = get_object_taxonomies( 'product_variation' ); 
  181.  
  182. if ( !empty( $all_taxs ) ) { 
  183. foreach ( $all_taxs as $tt ) { 
  184. if( isset( $tt->name ) ) { 
  185. $name = $tt->name; 
  186. }else{ 
  187. $name = $tt; 
  188.  
  189. $terms = get_the_terms( $original_variation_id, $name ); 
  190. if ( !empty( $terms ) ) { 
  191. $tax_sync = array(); 
  192. foreach ( $terms as $term ) { 
  193. if ( $this->sitepress->is_translated_taxonomy( $tt ) ) { 
  194. $term_id = apply_filters( 'translate_object_id', $term->term_id, $name, false, $lang ); 
  195. } else { 
  196. $term_id = $term->term_id; 
  197. if ( $term_id ) { 
  198. $tax_sync[] = intval( $term_id ); 
  199. //set the fourth parameter in 'true' because we need to add new terms, instead of replacing all 
  200. wp_set_object_terms( $tr_variation_id, $tax_sync, $name, true ); 
  201.  
  202. add_filter( 'terms_clauses', array( $this->sitepress, 'terms_clauses' ), 10, 4 ); 
  203.  
  204. public function duplicate_variation_data( $original_variation_id, $variation_id, $data, $lang, $trbl ) { 
  205. global $iclTranslationManagement; 
  206.  
  207. if( $this->woocommerce_wpml->sync_product_data->check_if_product_fields_sync_needed( $original_variation_id, $variation_id, 'postmeta_fields' ) || $trbl ) { 
  208. // custom fields 
  209. $settings = $iclTranslationManagement->settings[ 'custom_fields_translation' ]; 
  210. $all_meta = get_post_custom( $original_variation_id ); 
  211.  
  212. $post_fields = null; 
  213. foreach( $all_meta as $meta_key => $meta ) { 
  214.  
  215. foreach ( $meta as $meta_value ) { 
  216. // update current post variations meta 
  217.  
  218. if( ( substr( $meta_key, 0, 10 ) == 'attribute_' || isset( $settings[ $meta_key ] ) && $settings[ $meta_key ] == WPML_COPY_CUSTOM_FIELD ) ) { 
  219.  
  220. // adjust the global attribute slug in the custom field 
  221. $attid = null; 
  222. if( substr( $meta_key, 0, 10 ) == 'attribute_' ) { 
  223. $trn_post_meta = $this->woocommerce_wpml->attributes->get_translated_variation_attribute_post_meta( $meta_value, $meta_key, $original_variation_id, $variation_id, $lang ); 
  224. $meta_value = $trn_post_meta['meta_value']; 
  225. $meta_key = $trn_post_meta['meta_key']; 
  226.  
  227. if( $meta_key == '_stock') { 
  228. $this->update_stock_quantity( $variation_id, $meta_value ); 
  229. }else{ 
  230. update_post_meta( $variation_id, $meta_key, maybe_unserialize( $meta_value ) ); 
  231. }elseif ( !isset( $settings[ $meta_key ] ) || $settings[ $meta_key ] == WPML_IGNORE_CUSTOM_FIELD ) { 
  232. continue; 
  233.  
  234. //sync variation prices 
  235. if( 
  236. ( $this->woocommerce_wpml->settings[ 'enable_multi_currency' ] == WCML_MULTI_CURRENCIES_INDEPENDENT || $trbl ) && 
  237. in_array( $meta_key, array( '_sale_price', '_regular_price', '_price' ) ) 
  238. ) { 
  239. $meta_value = get_post_meta( $original_variation_id, $meta_key, true ); 
  240. update_post_meta( $variation_id, $meta_key, $meta_value ); 
  241.  
  242. if( isset( $settings[ $meta_key ] ) && $settings[ $meta_key ] == WPML_TRANSLATE_CUSTOM_FIELD ) { 
  243. //sync custom fields 
  244. $post_fields = $this->woocommerce_wpml->sync_product_data->sync_custom_field_value( $meta_key, $data, $variation_id, $post_fields, $original_variation_id, true ); 
  245.  
  246. //use direct query to update '_stock' to not trigger additional filters 
  247. public function update_stock_quantity( $variation_id, $meta_value ) { 
  248.  
  249. if( !get_post_meta( $variation_id, '_stock' ) ) { 
  250. $this->wpdb->insert( $this->wpdb->postmeta,  
  251. array( 
  252. 'meta_value' => $meta_value,  
  253. 'meta_key' => '_stock',  
  254. 'post_id' => $variation_id 
  255. ); 
  256. }else{ 
  257. $this->wpdb->update( $this->wpdb->postmeta,  
  258. array( 
  259. 'meta_value' => $meta_value 
  260. ),  
  261. array( 
  262. 'meta_key' => '_stock',  
  263. 'post_id' => $variation_id 
  264. ); 
  265.  
  266.  
  267. public function delete_removed_variation_attributes( $orig_product_id, $variation_id ) { 
  268.  
  269. $original_product_attr = get_post_meta( $orig_product_id, '_product_attributes', true ); 
  270.  
  271. $get_all_variation_attributes = $this->wpdb->get_results( 
  272. $this->wpdb->prepare( "SELECT * FROM {$this->wpdb->postmeta} WHERE post_id = %d AND meta_key LIKE 'attribute_%%' ",  
  273. $variation_id ) 
  274. ); 
  275.  
  276. foreach( $get_all_variation_attributes as $variation_attribute ) { 
  277. $attribute_name = substr( $variation_attribute->meta_key, 10 ); 
  278. if( !isset( $original_product_attr[ $attribute_name ] ) ) { 
  279. delete_post_meta( $variation_id, $variation_attribute->meta_key ); 
  280.  
  281.  
  282. public function get_product_variations( $product_id ) { 
  283.  
  284. $cache_key = $product_id; 
  285. $cache_group = 'product_variations'; 
  286. $temp_product_variations = wp_cache_get( $cache_key, $cache_group ); 
  287. if( $temp_product_variations ) return $temp_product_variations; 
  288.  
  289. $variations = $this->wpdb->get_results( 
  290. $this->wpdb->prepare( 
  291. "SELECT * FROM {$this->wpdb->posts} 
  292. WHERE post_status IN ('publish', 'private') 
  293. AND post_type = 'product_variation' 
  294. AND post_parent = %d ORDER BY ID",  
  295. $product_id ) 
  296. ); 
  297.  
  298. wp_cache_set( $cache_key, $variations, $cache_group ); 
  299.  
  300. return $variations; 
  301.  
  302. public function remove_translations_for_variations() { 
  303. check_ajax_referer( 'delete-variations', 'security' ); 
  304.  
  305. if ( ! current_user_can( 'edit_products' ) ) { 
  306. die(-1); 
  307. $variation_ids = (array) $_POST['variation_ids']; 
  308.  
  309. foreach ( $variation_ids as $variation_id ) { 
  310. $trid = $this->sitepress->get_element_trid( $variation_id, 'post_product_variation' ); 
  311. $translations = $this->sitepress->get_element_translations( $trid, 'post_product_variation' ); 
  312.  
  313. foreach ( $translations as $translation ) { 
  314. if ( !$translation->original ) { 
  315. wp_delete_post( $translation->element_id ); 
  316.  
  317. //update taxonomy in variations 
  318. public function update_taxonomy_in_variations() { 
  319. $original_element = filter_input( INPUT_POST, 'translation_of', FILTER_SANITIZE_NUMBER_INT ); 
  320. $taxonomy = filter_input( INPUT_POST, 'taxonomy', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 
  321. $language = filter_input( INPUT_POST, 'language', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 
  322. $slug = filter_input( INPUT_POST, 'slug', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 
  323. $name = filter_input( INPUT_POST, 'name', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 
  324. $term_id = $this->wpdb->get_var( 
  325. $this->wpdb->prepare( 
  326. "SELECT term_id FROM {$this->wpdb->term_taxonomy} WHERE term_taxonomy_id = %d" 
  327. , $original_element ) 
  328. ); 
  329. $original_term = $this->woocommerce_wpml->terms->wcml_get_term_by_id( $term_id, $taxonomy ); 
  330. $original_slug = $original_term->slug; 
  331. //get variations with original slug 
  332. $variations = $this->wpdb->get_results( 
  333. $this->wpdb->prepare( 
  334. "SELECT post_id FROM {$this->wpdb->postmeta} WHERE meta_key=%s AND meta_value = %s",  
  335. 'attribute_'.$taxonomy, $original_slug 
  336. ); 
  337.  
  338. foreach( $variations as $variation ) { 
  339. //update taxonomy in translation of variation 
  340. $trnsl_variation_id = apply_filters( 'translate_object_id', $variation->post_id, 'product_variation', false, $language ); 
  341. if( !is_null( $trnsl_variation_id ) ) { 
  342. if( !$slug ) { 
  343. $slug = sanitize_title( $name ); 
  344. update_post_meta( $trnsl_variation_id, 'attribute_'.$taxonomy, $slug ); 
  345.  
  346. public function remove_variation_ajax() { 
  347. if( isset( $_POST[ 'variation_id' ] ) ) { 
  348. $trid = $this->sitepress->get_element_trid( filter_input( INPUT_POST, 'variation_id', FILTER_SANITIZE_NUMBER_INT ), 'post_product_variation' ); 
  349. if( $trid ) { 
  350. $translations = $this->sitepress->get_element_translations( $trid, 'post_product_variation' ); 
  351. if( $translations ) { 
  352. foreach( $translations as $translation ) { 
  353. if( !$translation->original ) { 
  354. wp_delete_post( $translation->element_id, true ); 
  355.