PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.28
ووسلام – همگام سازی ووکامرس و باسلام v1.10.28
1.10.28 1.1.26 1.10.27 1.10.24 1.10.25 1.10.23 1.10.22 1.10.21 1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 All 61 releases
← All changes | includes/Admin/Product/Data/Services/VariantService.php +154 -16 1.10.21 → 1.10.28 View file →
@@ -1,4 +1,4 @@
1 1 <?php
2 2
3 3 namespace SyncBasalam\Admin\Product\Data\Services;
4 4
@@ -16,23 +16,161 @@
16 16 $this->priceService = new PriceService();
17 17 $this->settings = syncBasalamSettings()->getSettings();
18 18 }
19 19
20 - public function getVariants($product): array
21 - {
22 - if (!$product instanceof \WC_Product_Variable) return [];
20 + public function getVariants($product): array
21 + {
22 + if (!$product instanceof \WC_Product_Variable) return [];
23 +
24 + return array_column($this->getVariantEntries($product), 'data');
25 + }
26 +
27 + private function getVariantEntries($product): array
28 + {
29 + $entries = [];
30 + $seen = [];
31 +
32 + // Children follow WooCommerce's menu order: the first matching variation
33 + // owns a combination when a specific variation overlaps an "Any" one.
34 + foreach ($product->get_children() as $variationId) {
35 + $variation = wc_get_product($variationId);
36 + if (!$variation || $variation->get_status() !== 'publish') continue;
37 +
38 + $combinations = $this->expandVariationAttributes($variation, $product);
39 + $variant = $this->createVariant($variationId, $product);
40 + $mapping = get_post_meta($variationId, 'sync_basalam_variation_map', true);
41 + $isWildcard = $this->isWildcardVariation($variation, $product);
42 +
43 + foreach ($combinations as $attributes) {
44 + $keyAttributes = $attributes;
45 + ksort($keyAttributes);
46 + $key = wp_json_encode($keyAttributes);
47 + if (isset($seen[$key])) continue;
48 + $seen[$key] = true;
49 + if (!$variant) continue;
50 +
51 + $data = $variant;
52 + $data['properties'] = $this->getVariantProperties($variation, $product, $attributes);
53 + $propertyKey = $this->getPropertyKey($data['properties']);
54 + if (is_array($mapping)) {
55 + unset($data['id']);
56 + if (!empty($mapping[$propertyKey])) $data['id'] = $mapping[$propertyKey];
57 + } elseif ($isWildcard) {
58 + // Rebuild legacy empty-property variants through a product PATCH.
59 + unset($data['id']);
60 + }
61 +
62 + $entries[] = ['variation_id' => $variationId, 'data' => $data];
63 + }
64 + }
65 +
66 + return $entries;
67 + }
68 +
69 + private function expandVariationAttributes($variation, $product): array
70 + {
71 + $selected = $variation->get_variation_attributes();
72 + if (!$product->get_variation_attributes()) return [];
73 + $combinations = [[]];
74 +
75 + foreach ($product->get_variation_attributes() as $name => $options) {
76 + $attributeName = 'attribute_' . sanitize_title($name);
77 + $value = $selected[$attributeName] ?? '';
78 + $values = $value === '' ? $options : [$value];
79 + $values = array_values(array_unique(array_filter($values, static function ($option) {
80 + return trim((string) $option) !== '';
81 + })));
82 +
83 + $expanded = [];
84 + foreach ($combinations as $combination) {
85 + foreach ($values as $option) {
86 + $expanded[] = array_merge($combination, [$attributeName => (string) $option]);
87 + }
88 + }
89 + $combinations = $expanded;
90 + }
91 +
92 + return $combinations;
93 + }
94 +
95 + private function isWildcardVariation($variation, $product): bool
96 + {
97 + $attributes = $variation->get_variation_attributes();
98 + foreach ($product->get_variation_attributes() as $name => $options) {
99 + if (($attributes['attribute_' . sanitize_title($name)] ?? '') === '') return true;
100 + }
101 +
102 + return false;
103 + }
104 +
105 + private function needsExpandedMapping($product): bool
106 + {
107 + if (!$product instanceof \WC_Product_Variable) return false;
108 +
109 + foreach ($product->get_children() as $variationId) {
110 + $variation = wc_get_product($variationId);
111 + if (!$variation) continue;
112 + if ($this->isWildcardVariation($variation, $product)) return true;
113 + if (get_post_meta($variationId, 'sync_basalam_variation_map', true)) return true;
114 + }
115 +
116 + return false;
117 + }
118 +
119 + /** Map every expanded remote variant back to its original WooCommerce variation. */
120 + public function syncExpandedVariationIds($product, array $variants): void
121 + {
122 + if (!$this->needsExpandedMapping($product)) return;
123 +
124 + $remoteIds = [];
125 + foreach ($variants as $variant) {
126 + if (!empty($variant['id'])) {
127 + $remoteIds[$this->getPropertyKey($variant['properties'] ?? [])] = $variant['id'];
128 + }
129 + }
130 +
131 + $mappings = [];
132 + foreach ($this->getVariantEntries($product) as $entry) {
133 + $key = $this->getPropertyKey($entry['data']['properties']);
134 + if (isset($remoteIds[$key])) $mappings[$entry['variation_id']][$key] = $remoteIds[$key];
135 + }
136 +
137 + foreach ($product->get_children() as $variationId) {
138 + delete_post_meta($variationId, 'sync_basalam_variation_id');
139 + delete_post_meta($variationId, 'sync_basalam_variation_map');
140 + if (empty($mappings[$variationId])) continue;
141 +
142 + update_post_meta($variationId, 'sync_basalam_variation_map', $mappings[$variationId]);
143 + // Separate rows keep the existing order lookup working for every id.
144 + foreach (array_unique($mappings[$variationId]) as $remoteId) {
145 + add_post_meta($variationId, 'sync_basalam_variation_id', $remoteId);
146 + }
147 + }
148 + }
149 +
150 + private function getPropertyKey(array $properties): string
151 + {
152 + $values = [];
153 + foreach ($properties as $property) {
154 + $name = $property['property'] ?? '';
155 + $value = $property['value'] ?? '';
156 + if (is_array($name)) $name = $name['title'] ?? $name['name'] ?? '';
157 + if (is_array($value)) $value = $value['title'] ?? $value['name'] ?? '';
158 + $values[$this->normalizeProperty($name)] = $this->normalizeProperty($value);
159 + }
160 + ksort($values);
161 +
162 + return wp_json_encode($values);
163 + }
164 +
165 + private function normalizeProperty(string $value): string
166 + {
167 + $value = mb_strtolower(trim(rawurldecode($value)), 'UTF-8');
168 + $value = str_replace(['ي', 'ك', '-', '_', '–', '—'], ['ی', 'ک', ' ', ' ', ' ', ' '], $value);
169 +
170 + return preg_replace('/\s+/u', ' ', $value);
171 + }
23 172
24 - $variants = [];
25 - $variationIds = $product->get_children();
26 -
27 - foreach ($variationIds as $variationId) {
28 - $variant = $this->createVariant($variationId, $product);
29 - if ($variant) $variants[] = $variant;
30 - }
31 -
32 - return $variants;
33 - }
34 -
35 173 private function createVariant(int $variationId, $parentProduct): ?array
36 174 {
37 175 $variation = wc_get_product($variationId);
38 176 if (!$variation) return null;
@@ -97,12 +235,12 @@
97 235
98 236 return [$stock, $stockStatus];
99 237 }
100 238
101 - private function getVariantProperties($variation, $parentProduct): array
239 + private function getVariantProperties($variation, $parentProduct, ?array $attributes = null): array
102 240 {
103 241 $properties = [];
104 - $variationData = $variation->get_variation_attributes();
242 + $variationData = $attributes ?? $variation->get_variation_attributes();
105 243
106 244 foreach ($variationData as $attributeName => $attributeValue) {
107 245 $taxonomyName = str_replace('attribute_', '', $attributeName);
108 246 $attributeLabel = str_replace(['pa_', '-'], ' ', wc_attribute_label($taxonomyName, $parentProduct));