PluginProbe ʕ •ᴥ•ʔ
WPML Multilingual & Multicurrency for WooCommerce / trunk
WPML Multilingual & Multicurrency for WooCommerce vtrunk
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 / VariationMeta.php
woocommerce-multilingual / classes / Synchronization / Component Last commit date
Attachments.php 1 year ago Attributes.php 4 months ago DownloadableFiles.php 4 months ago Linked.php 10 months ago Meta.php 1 year ago Post.php 3 months ago Stock.php 1 year ago Synchronizer.php 1 year ago SynchronizerForMeta.php 8 months ago Taxonomies.php 4 months ago VariationAttachments.php 1 year ago VariationMeta.php 8 months ago VariationTaxonomies.php 8 months ago Variations.php 1 month ago
VariationMeta.php
218 lines
1 <?php
2
3 namespace WCML\Synchronization\Component;
4
5 use WCML\Utilities\DB;
6 use WCML\Utilities\SyncHash;
7 use WPML\FP\Obj;
8 use WPML_Post_Custom_Field_Setting_Keys;
9
10 class VariationMeta extends SynchronizerForMeta {
11
12 /**
13 * @param \WP_Post $variation
14 * @param int[] $translationsIds
15 * @param array<int,string> $translationsLanguages
16 */
17 public function run( $variation, $translationsIds, $translationsLanguages ) {
18 $delayedFields = [];
19 foreach ( $translationsLanguages as $translationId => $language ) {
20 $delayedFields = $this->synchronizeVariationMeta( $variation->ID, $translationId, $language, $delayedFields );
21 }
22 $this->processDelayedFields( $delayedFields, $translationsIds );
23 $this->deleteOrphanedFields( $variation->ID, $translationsIds );
24 }
25
26 /**
27 * @param int $variationId
28 * @param int $translationId
29 * @param string $language
30 * @param array $delayedFields
31 *
32 * @return array
33 */
34 protected function synchronizeVariationMeta( $variationId, $translationId, $language, $delayedFields ) {
35 $variationMeta = get_post_custom( $variationId );
36 unset( $variationMeta[ SyncHash::META_KEY ] );
37 $currentHash = $this->getCurrentHash( $variationMeta, $variationId, $translationId, $language );
38 $isSyncNeeded = $this->syncHashManager->isNewGroupValue( $translationId, SyncHash::GROUP_FIELDS, $currentHash );
39
40 if ( ! $isSyncNeeded ) {
41 return [];
42 }
43
44 global $iclTranslationManagement;
45 $settings = $iclTranslationManagement->settings['custom_fields_translation'];
46 $excludedKeys = WPML_Post_Custom_Field_Setting_Keys::get_excluded_keys();
47
48 foreach ( $variationMeta as $metaKey => $meta ) {
49 if ( in_array( $metaKey, $excludedKeys, true ) ) {
50 continue;
51 }
52
53 $metaValue = reset( $meta );
54 if ( false === $metaValue ) {
55 $metaValue = '';
56 }
57
58 if ( substr( $metaKey, 0, 10 ) === 'attribute_' ) {
59 if ( '' !== $metaValue ) {
60 $trn_post_meta = $this->woocommerceWpml->attributes->get_translated_variation_attribute_post_meta( $metaValue, $metaKey, $variationId, $translationId, $language );
61 $metaValue = $trn_post_meta['meta_value'];
62 $metaKey = $trn_post_meta['meta_key'];
63 } else {
64 $metaValue = '';
65 }
66 $delayedFields[] = [
67 'post_id' => $translationId,
68 'meta_key' => $metaKey,
69 'meta_value' => maybe_unserialize( $metaValue ),
70 ];
71 continue;
72 }
73
74 if ( ! isset( $settings[ $metaKey ] ) || (int) $settings[ $metaKey ] === WPML_IGNORE_CUSTOM_FIELD ) {
75 continue;
76 }
77
78 if (
79 in_array( $metaKey, [ '_sale_price', '_regular_price', '_price' ] ) &&
80 (int) $this->woocommerceWpml->settings['enable_multi_currency'] === WCML_MULTI_CURRENCIES_INDEPENDENT
81 ) {
82 $delayedFields[] = [
83 'post_id' => $translationId,
84 'meta_key' => $metaKey,
85 'meta_value' => $metaValue,
86 ];
87 continue;
88 }
89
90 if ( (int) Obj::prop( $metaKey, $settings ) === WPML_COPY_CUSTOM_FIELD ) {
91 $delayedFields[] = [
92 'post_id' => $translationId,
93 'meta_key' => $metaKey,
94 'meta_value' => maybe_unserialize( $metaValue ),
95 ];
96 continue;
97 }
98 }
99
100 $this->syncHashManager->updateGroupValue( $translationId, SyncHash::GROUP_FIELDS, $currentHash );
101
102 return $delayedFields;
103 }
104
105 /**
106 * @param array $delayedFields
107 * @param int[] $translationsIds
108 */
109 private function processDelayedFields( $delayedFields, $translationsIds ) {
110 if ( empty( $delayedFields ) ) {
111 return;
112 }
113
114 $metaRawData = $this->wpdb->get_results(
115 "
116 SELECT meta_id, post_id, meta_key
117 FROM {$this->wpdb->postmeta}
118 WHERE post_id IN (" . DB::prepareIn( $translationsIds, '%d' ) . ")
119 "
120 );
121
122 $metaData = [];
123 foreach ( $metaRawData as $metaEntry ) {
124 $metaData[ $metaEntry->post_id ][ $metaEntry->meta_id ] = $metaEntry->meta_key;
125 }
126
127 $delayedFieldsActions = [];
128 foreach ( $delayedFields as $delayedFieldData ) {
129 $fieldPostId = $delayedFieldData['post_id'];
130 $fieldMetaKey = $delayedFieldData['meta_key'];
131 $fieldMetaValue = $delayedFieldData['meta_value'];
132 $delayedFieldsActions[ $fieldMetaKey ]['meta_value'] = $fieldMetaValue;
133 $metaDataByVariationId = Obj::propOr( [], $fieldPostId, $metaData );
134 if ( in_array( $fieldMetaKey, $metaDataByVariationId, true ) ) {
135 $fieldMetaIds = array_keys( $metaDataByVariationId, $fieldMetaKey, true );
136 if ( count( $fieldMetaIds ) > 1 ) {
137 $delayedFieldsActions[ $fieldMetaKey ]['delete'][ $fieldPostId ] = $fieldMetaIds;
138 $delayedFieldsActions[ $fieldMetaKey ]['insert'][ $fieldPostId ] = $fieldMetaValue;
139 } else {
140 $fieldMetaValueHash = md5( maybe_serialize( $fieldMetaValue ) );
141 $delayedFieldsActions[ $fieldMetaKey ]['update'][ $fieldMetaValueHash ][ $fieldPostId ] = $fieldMetaValue;
142 }
143 } else {
144 $delayedFieldsActions[ $fieldMetaKey ]['insert'][ $fieldPostId ] = $fieldMetaValue;
145 }
146 }
147
148 // Perform delete/insert/update actions.
149 foreach ( $delayedFieldsActions as $delayedFieldMetaKey => $delayedFieldMetaData ) {
150 // Delete all entries that have duplicated values:
151 // all the related meta fields should have unique values.
152 $dataToDelete = Obj::propOr( [], 'delete', $delayedFieldMetaData );
153 if ( ! empty( $dataToDelete ) ) {
154 $metaIdsToDelete = [];
155 foreach ( $dataToDelete as $itemMetaIdsToDelete ) {
156 $metaIdsToDelete = array_merge( $metaIdsToDelete, $itemMetaIdsToDelete );
157 }
158 $this->deleteMetaByIds( $metaIdsToDelete );
159 // phpcs:disable WordPress.WP.PreparedSQL.NotPrepared
160 $this->wpdb->query(
161 "
162 DELETE FROM {$this->wpdb->postmeta}
163 WHERE meta_id IN (" . DB::prepareIn( $metaIdsToDelete, '%d' ) . ")
164 "
165 );
166 // phpcs:enable
167 }
168
169 // Insert all post_id/meta_key/meta_value groups at once, per meta_key.
170 // For each meta key, data is made of pairs [ post ID => metaValue ] for easier insertion.
171 // This ensures that the number of values inserted on each batch is, at most, the number of variations.
172 $dataToInsert = Obj::propOr( [], 'insert', $delayedFieldMetaData );
173 if ( ! empty( $dataToInsert )) {
174 $this->insertMeta( $delayedFieldMetaKey, $dataToInsert );
175 }
176
177 // Update all variations at once.
178 // For each meta key, data is made of pairs [ meta value => list of affected post IDs ] so it is easier to compose IN statements.
179 $dataToUpdate = Obj::propOr( [], 'update', $delayedFieldMetaData );
180 if ( ! empty( $dataToUpdate ) ) {
181 foreach ( $dataToUpdate as $itemsPerValue ) {
182 $idsToUpdate = array_values( array_unique( array_map( 'intval', array_keys( $itemsPerValue ) ) ) );
183 $updateMetaValue = reset( $itemsPerValue );
184 $this->unifyMeta( $delayedFieldMetaKey, $updateMetaValue, $idsToUpdate );
185 }
186 }
187 }
188 }
189
190 /**
191 * @param array $variationMeta
192 * @param int $variationId
193 * @param int $translationId
194 * @param string $language
195 *
196 * @return string
197 */
198 private function getCurrentHash( $variationMeta, $variationId, $translationId, $language ) {
199 $translationMeta = $variationMeta;
200 foreach ( $variationMeta as $metaKey => $meta ) {
201 if ( substr( $metaKey, 0, 10 ) !== 'attribute_' ) {
202 continue;
203 }
204 $metaValue = reset( $meta );
205 if ( false === $metaValue ) {
206 continue;
207 }
208 $trn_post_meta = $this->woocommerceWpml->attributes->get_translated_variation_attribute_post_meta( $metaValue, $metaKey, $variationId, $translationId, $language );
209 $metaValue = $trn_post_meta['meta_value'];
210 $metaKey = $trn_post_meta['meta_key'];
211 $translationMeta[ $metaKey ] = [ $metaValue ];
212 }
213 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.serialize_serialize
214 return md5( serialize( $translationMeta ) );
215 }
216
217 }
218