PluginProbe ʕ •ᴥ•ʔ
WPML Multilingual & Multicurrency for WooCommerce / 5.5.7
WPML Multilingual & Multicurrency for WooCommerce v5.5.7
5.5.7 5.3.8 5.3.9 5.4.3 5.4.4 5.4.5 5.5.1 5.5.1.1 5.5.2.2 5.5.2.3 5.5.3 5.5.3.1 5.5.4 5.5.5 5.5.6 trunk 0.9 1.0 1.1 1.2 1.3 1.4 1.5 2.0 2.2 2.3 2.3.1 2.3.2 3.0 3.0.1 3.1 3.2 3.2.1 3.2.2 3.3 3.3.1 3.3.2 3.3.3 3.3.4 3.4 3.4.1 3.4.2 3.4.3 3.5 3.5.1 3.5.2 3.5.3 3.5.4 3.5.5 3.6 3.6.1 3.6.10 3.6.11 3.6.2 3.6.3 3.6.4 3.6.5 3.6.5.1 3.6.6 3.6.7 3.6.8 3.6.9 3.7 3.7.1 3.7.10 3.7.11 3.7.12 3.7.13 3.7.14 3.7.15 3.7.16 3.7.2 3.7.3 3.7.4 3.7.5 3.7.6 3.7.7 3.7.8 3.7.9 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.9.0 3.9.1 3.9.1.1 3.9.2 3.9.3 3.9.4 3.9.5 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10.0 4.10.1 4.10.2 4.10.3 4.10.4 4.11.2 4.11.3.2 4.11.5 4.11.6 4.12.1 4.12.4 4.12.5 4.12.6 4.2.0 4.2.0.1 4.2.1 4.2.1.1 4.2.10 4.2.2 4.2.3 4.2.4 4.2.5 4.2.6 4.2.7 4.2.7.1 4.2.8 4.2.8.1 4.2.9 4.3.0 4.3.1 4.3.2 4.3.2.1 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.4.0 4.4.1 4.4.2 4.4.2.1 4.5.0 4.6.0 4.6.1 4.6.2 4.6.2.1 4.6.3 4.6.5 4.6.6 4.6.7 4.7.0 4.7.1 4.7.2 4.7.3 4.7.4 4.7.5 4.7.6 4.7.7 4.7.8 4.7.9 4.8.0 4.9.0 4.9.1 5.0.1 5.0.2 5.1.1 5.1.2 5.1.3 5.2.0 5.2.1 5.3.2 5.3.3.1 5.3.4 5.3.5 5.3.6 5.3.7
woocommerce-multilingual / classes / Synchronization / Component / Variations.php
woocommerce-multilingual / classes / Synchronization / Component Last commit date
Attachments.php 1 week ago Attributes.php 1 week ago DownloadableFiles.php 1 week ago Linked.php 1 week ago Meta.php 1 week ago Post.php 1 week ago Stock.php 1 week ago Synchronizer.php 1 week ago SynchronizerForMeta.php 1 week ago Taxonomies.php 1 week ago VariationAttachments.php 1 week ago VariationMeta.php 1 week ago VariationTaxonomies.php 1 week ago Variations.php 1 week ago
Variations.php
237 lines
1 <?php
2
3 namespace WCML\Synchronization\Component;
4
5 use WCML\Synchronization\Hooks;
6 use WCML\Utilities\DB;
7 use WCML\Utilities\SyncHash;
8 use WPML\FP\Obj;
9
10 class Variations extends SynchronizerForMeta {
11
12 public function run( $product, $translationsIds, $translationsLanguages ) {
13 $isVariableProduct = $this->woocommerceWpml->products->is_variable_product( $product->ID );
14 if ( ! $isVariableProduct ) {
15 return;
16 }
17
18 $productsIds = array_merge( [ $product->ID ], $translationsIds );
19 $storedVariations = [];
20 $storedRawVariations = $this->wpdb->get_results(
21 $this->wpdb->prepare(
22 "
23 SELECT * FROM {$this->wpdb->posts}
24 WHERE post_status IN ('publish','private')
25 AND post_type = %s
26 AND post_parent IN (" . DB::prepareIn( $productsIds ) . ")
27 ",
28 'product_variation'
29 )
30 );
31 foreach ( $storedRawVariations as $rawVariation ) {
32 $storedVariations[ $rawVariation->post_parent ][ $rawVariation->ID ] = $rawVariation;
33 }
34
35 $productVariations = $storedVariations[ $product->ID ] ?? null;
36 if ( ! $productVariations ) {
37 return;
38 }
39
40 $editorScopedIds = apply_filters( 'wcml_editor_scoped_variation_ids', null, $product->ID );
41 if ( is_array( $editorScopedIds ) ) {
42 if ( empty( $editorScopedIds ) ) {
43 return;
44 }
45
46 $productVariations = array_intersect_key( $productVariations, array_flip( $editorScopedIds ) );
47 }
48
49 $variationsTranslations = [];
50 $preparedVariationsToSetAsDuplication = [];
51
52 remove_action( 'save_post', [ $this->elementTranslations, 'save_post_actions' ], 100 );
53
54 foreach ( $productVariations as $productVariation ) {
55 foreach ( $translationsLanguages as $translationId => $language ) {
56 $variationTranslationId = $this->elementTranslations->element_id_in( $productVariation->ID, $language, false );
57 if ( $variationTranslationId ) {
58 $this->wpdb->update(
59 $this->wpdb->posts,
60 [
61 'post_status' => $productVariation->post_status,
62 'post_modified' => $productVariation->post_modified,
63 'post_modified_gmt' => $productVariation->post_modified_gmt,
64 'post_parent' => $translationId,
65 'menu_order' => $productVariation->menu_order,
66 ],
67 [ 'ID' => $variationTranslationId ]
68 );
69 unset( $storedVariations[ $translationId ][ $variationTranslationId ] );
70 $this->syncHashManager->initialize( $variationTranslationId, SyncHash::SOURCE_META );
71 } else {
72 $translationGui = str_replace( (string) $product->ID, (string) $translationId, $productVariation->guid );
73 $translationSlug = str_replace( (string) $product->ID, (string) $translationId, $productVariation->post_name );
74 $variationTranslationId = wp_insert_post(
75 [
76 'post_author' => $productVariation->post_author,
77 'post_date_gmt' => $productVariation->post_date_gmt,
78 'post_content' => $productVariation->post_content,
79 'post_title' => $productVariation->post_title,
80 'post_excerpt' => $productVariation->post_excerpt,
81 'post_status' => $productVariation->post_status,
82 'comment_status' => $productVariation->comment_status,
83 'ping_status' => $productVariation->ping_status,
84 'post_password' => $productVariation->post_password,
85 'post_name' => $translationSlug,
86 'to_ping' => $productVariation->to_ping,
87 'pinged' => $productVariation->pinged,
88 'post_modified' => $productVariation->post_modified,
89 'post_modified_gmt' => $productVariation->post_modified_gmt,
90 'post_content_filtered' => $productVariation->post_content_filtered,
91 'post_parent' => $translationId,
92 'guid' => $translationGui,
93 'menu_order' => $productVariation->menu_order,
94 'post_type' => $productVariation->post_type,
95 'post_mime_type' => $productVariation->post_mime_type,
96 'comment_count' => $productVariation->comment_count,
97 ]
98 );
99 $trid = $this->sitepress->get_element_trid( $productVariation->ID, 'post_product_variation' );
100 $this->sitepress->set_element_language_details( $variationTranslationId, 'post_product_variation', $trid, $language );
101 $preparedVariationsToSetAsDuplication[] = $this->wpdb->prepare( "(%d,%s,%s)", $variationTranslationId, '_wcml_duplicate_of_variation', $productVariation->ID );
102 $this->syncHashManager->initialize( $variationTranslationId, SyncHash::SOURCE_EMPTY, true );
103 }
104 $variationsTranslations[ $productVariation->ID ][ $variationTranslationId ] = $language;
105 }
106 }
107
108 if ( ! empty( $preparedVariationsToSetAsDuplication ) ) {
109 $this->wpdb->query(
110 "INSERT INTO {$this->wpdb->postmeta}
111 (`post_id`,`meta_key`,`meta_value`)
112 VALUES " . implode( ',', $preparedVariationsToSetAsDuplication )
113 );
114 }
115
116 if ( ! $editorScopedIds ) {
117 foreach ( $translationsIds as $translationId ) {
118 $orphanedTranslationVariations = $storedVariations[ $translationId ] ?? [];
119 foreach ( $orphanedTranslationVariations as $orphanedTranslationVariationId => $orphanedTranslationVariation ) {
120 wp_delete_post( $orphanedTranslationVariationId, true );
121 }
122 }
123 }
124
125 $this->removeOrphanedVariationAttributes( $product->ID, array_keys( $productVariations ) );
126 $this->synchronizeMinMaxPrices( $product->ID, $translationsLanguages, $variationsTranslations );
127
128 $wcmlProductDataStore = wcml_product_data_store_cpt();
129 foreach ( $variationsTranslations as $variationId => $variationTranslationsLanguages ) {
130 $variationTranslations = array_keys( $variationTranslationsLanguages );
131 do_action( Hooks::HOOK_SYNCHRONIZE_PRODUCT_VARIATION_TRANSLATIONS, $productVariations[ $variationId ], $variationTranslations, $variationTranslationsLanguages );
132 foreach ( $variationTranslations as $variationTranslationId ) {
133 $this->syncHashManager->saveHash( $variationTranslationId, true );
134 }
135 }
136
137 add_action( 'save_post', [ $this->elementTranslations, 'save_post_actions' ], 100, 2 );
138 }
139
140 private function removeOrphanedVariationAttributes( $productId, $productVariations ) {
141 $productAttributes = get_post_meta( $productId, '_product_attributes', true );
142 $variationsAttributes = $this->wpdb->get_results(
143 $this->wpdb->prepare(
144 "
145 SELECT meta_id, meta_key
146 FROM {$this->wpdb->postmeta}
147 WHERE meta_key LIKE %s
148 AND post_id IN (" . DB::prepareIn( $productVariations ) . ")
149 ",
150 'attribute_%%'
151 ),
152 OBJECT_K
153 );
154
155 $metaIdsToDelete = [];
156 foreach ( $variationsAttributes as $variationsAttribute ) {
157 $attributeName = substr( $variationsAttribute->meta_key, 10 );
158 if ( ! isset( $productAttributes[ $attributeName ] ) ) {
159 $metaIdsToDelete[] = $variationsAttribute->meta_id;
160 }
161 }
162
163 if ( empty( $metaIdsToDelete ) ) {
164 return;
165 }
166
167 $this->deleteMetaByIds( $metaIdsToDelete );
168 }
169
170 private function synchronizeMinMaxPrices( $productId, $translationsLanguages, $variationsTranslations ) {
171 $productsIds = array_merge( [ $productId ], array_keys( $translationsLanguages ) );
172
173 $storedRawData = $this->wpdb->get_results(
174 $this->wpdb->prepare(
175 "
176 SELECT post_id, meta_key, meta_value
177 FROM {$this->wpdb->postmeta}
178 WHERE meta_key IN (
179 '_min_price_variation_id',
180 '_max_price_variation_id',
181 '_min_regular_price_variation_id',
182 '_max_regular_price_variation_id',
183 '_min_sale_price_variation_id',
184 '_max_sale_price_variation_id'
185 )
186 AND post_id IN (" . DB::prepareIn( $productsIds ) . ")
187 LIMIT %d
188 ",
189 count( $productsIds ) * 6
190 )
191 );
192
193 $storedData = [];
194 foreach ( $storedRawData as $rawData ) {
195 $storedData[ $rawData->post_id ][ $rawData->meta_key ] = $rawData->meta_value;
196 }
197
198 $productMinMaxVariationsData = $storedData[ $productId ] ?? null;
199
200 if ( null === $productMinMaxVariationsData ) {
201 return;
202 }
203
204 $metaToInsert = [];
205 $metaToUpdate = [];
206 foreach ( $translationsLanguages as $translationId => $language ) {
207 $translationMinMaxVariationsData = $storedData[ $translationId ] ?? [];
208 foreach ( $productMinMaxVariationsData as $minMaxKey => $minMaxVariationId ) {
209 $minMaxVariationTranslations = $variationsTranslations[ $minMaxVariationId ] ?? [];
210 $minMaxVariationTranslationsByLanguage = array_flip( $minMaxVariationTranslations );
211 if ( ! Obj::prop( $language, $minMaxVariationTranslationsByLanguage ) ) {
212 continue;
213 }
214 $translationMinMaxVariationId = $translationMinMaxVariationsData[ $minMaxKey ] ?? null;
215 if ( null === $translationMinMaxVariationId ) {
216 $metaToInsert[ $minMaxKey ][ $translationId ] = Obj::prop( $language, $minMaxVariationTranslationsByLanguage );
217 continue;
218 }
219
220 if ( $language === Obj::prop( $translationMinMaxVariationId, $minMaxVariationTranslations ) ) {
221 continue;
222 }
223 $metaToUpdate[ $minMaxKey ][ $translationId ] = Obj::prop( $language, $minMaxVariationTranslationsByLanguage );
224 }
225 }
226
227 foreach ( $metaToInsert as $metaKey => $metaData ) {
228 $this->insertMeta( $metaKey, $metaData );
229 }
230
231 foreach ( $metaToUpdate as $metaKey => $metaData ) {
232 $this->updateMeta( $metaKey, $metaData );
233 }
234 }
235
236 }
237