PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.9
ووسلام – همگام سازی ووکامرس و باسلام v1.10.9
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 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 All 53 releases
sync-basalam / includes / Services / Products / UpdateSingleProductService.php

UpdateSingleProductService.php in ووسلام – همگام سازی ووکامرس و باسلام 1.10.9, at includes/Services/Products/UpdateSingleProductService.php

266 lines 10.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SyncBasalam\Services\Products;
4
5 use SyncBasalam\Config\Endpoints;
6 use SyncBasalam\Services\ApiServiceManager;
7 use SyncBasalam\Jobs\Exceptions\RetryableException;
8 use SyncBasalam\Jobs\Exceptions\NonRetryableException;
9 use SyncBasalam\Utilities\ProductMetaKey;
10
11 defined('ABSPATH') || exit;
12
13 class UpdateSingleProductService
14 {
15 private $apiservice;
16 private $variationsService;
17
18 public function __construct()
19 {
20 $this->apiservice = syncBasalamContainer()->get(ApiServiceManager::class);
21 $this->variationsService = syncBasalamContainer()->get(UpdateProductVariationsService::class);
22 }
23
24 public function updateProductInBasalam($productData, $productId)
25 {
26 if (!get_post_type($productId) === 'product') throw NonRetryableException::invalidData('نوع post �
27 حصول نیست.');
28
29 $productData = apply_filters('sync_basalam_product_data_before_update', $productData, $productId);
30
31 do_action('sync_basalam_before_update_product_api', $productId, $productData);
32
33 $syncBasalamProductId = get_post_meta($productId, ProductMetaKey::basalamProductId(), true);
34 ProductConnection::assertUnique($productId, $syncBasalamProductId);
35
36 // Variable products whose variations are all connected to Basalam are updated with one
37 // request per variation, so price and stock must not be part of the product payload.
38 if ($this->shouldUpdateVariationsSeparately($productId, $productData)) {
39 $this->variationsService->updateVariations($syncBasalamProductId, $productData['variants'], $productId);
40
41 unset($productData['variants'], $productData['primary_price'], $productData['stock']);
42
43 if (!$this->hasProductFieldsToUpdate($productData)) {
44 return $this->finishUpdate($productId, [], '�
45 تغیرهای �
46 حصول با �
47 وفقیت بروزرسانی شدند.');
48 }
49 }
50 $url = sprintf(Endpoints::PRODUCT_UPDATE, $syncBasalamProductId);
51
52 $maxDescriptionRetries = 3;
53 $descriptionRetry = 0;
54
55 while (true) {
56 try {
57 $request = $this->apiservice->patch($url, $productData);
58 break;
59 } catch (RetryableException $e) {
60 throw $e;
61 } catch (NonRetryableException $e) {
62 if ($descriptionRetry < $maxDescriptionRetries && $this->stripForbiddenDescription($e, $productData, $productId, $descriptionRetry)) {
63 $descriptionRetry++;
64 continue;
65 }
66 throw $e;
67 } catch (\Exception $e) {
68 throw new \Exception(esc_html('خطا در ارتباط با API باسلا�
69 : ' . $e->getMessage()));
70 }
71 }
72
73 $body = $request['body'] ?? '';
74
75 if (is_string($body)) $body = json_decode($body, true);
76
77 if ($request['status_code'] != 200) {
78 if ($request['status_code'] == 403) throw NonRetryableException::unauthorized("این �
79 حصول �
80 تعلق به غرفه فعلی نیست.");
81
82 if (!is_array($body)) $body = [];
83
84 if (isset($body['messages'][0]['message'])) $message = $body['messages'][0]['message'];
85 elseif (isset($body[0]['message'])) $message = $body[0]['message'];
86 else $message = '';
87
88 if (isset($body['messages'][0]['fields'][0])) $field = $body['messages'][0]['fields'][0];
89 elseif (isset($body[0]['fields'][0])) $field = $body[0]['fields'][0];
90 else $field = '';
91
92 $errorMessage = $message ?: 'درخواست با خطا �
93 واجه شد.';
94 if ($field) $errorMessage .= ' (فیلد: ' . $field . ')';
95
96 throw NonRetryableException::permanent(esc_html($errorMessage));
97 }
98
99 if (is_wp_error($request)) throw NonRetryableException::permanent('خطایی در ارتباط با سرور رخ داد.');
100
101 $product = \wc_get_product($productId);
102 if ($product && $product->is_type('variable')) {
103 $variations = $product->get_children();
104 if (isset($body['variants'])) {
105 $wcVariations = [];
106 $attributes = $product->get_attributes();
107
108 foreach ($variations as $variationId) {
109 $variation = \wc_get_product($variationId);
110 $attributeValues = [];
111
112 foreach ($attributes as $attributeName => $attribute) {
113 if ($attribute->get_variation()) {
114 $cleanAttributeName = str_replace('attribute_', '', $attributeName);
115 $value = $variation->get_attribute($cleanAttributeName);
116
117 $value = urldecode($value);
118 $value = trim($value);
119 $value = mb_strtolower($value, 'UTF-8');
120 $value = str_replace(['ي', 'ك'], ['ی', 'ک'], $value);
121 $value = str_replace(['-', '_', '', ''], ' ', $value);
122 $value = preg_replace('/\s+/', ' ', $value);
123
124 if (!empty($value)) $attributeValues[] = $value;
125 }
126 }
127
128 if (!empty($attributeValues)) {
129 $key = implode("_", $attributeValues);
130 $wcVariations[$key] = $variationId;
131 }
132 }
133
134 $syncBasalamVariations = [];
135 foreach ($body['variants'] as $variant) {
136 $attributeValues = [];
137 if (!empty($variant['properties'])) {
138 foreach ($variant['properties'] as $property) {
139 $val = $property['value']['title'];
140
141 $val = trim($val);
142 $val = mb_strtolower($val, 'UTF-8');
143 $val = str_replace(['ي', 'ك'], ['ی', 'ک'], $val);
144 $val = str_replace(['-', '_', '', ''], ' ', $val);
145 $val = preg_replace('/\s+/', ' ', $val);
146
147 if (!empty($val)) $attributeValues[] = $val;
148 }
149 }
150
151 if (!empty($attributeValues)) {
152 $key = implode("_", $attributeValues);
153 $syncBasalamVariations[$key] = $variant['id'];
154 }
155 }
156
157 foreach ($wcVariations as $key => $wcVarId) {
158 if (isset($syncBasalamVariations[$key])) {
159 update_post_meta($wcVarId, 'sync_basalam_variation_id', $syncBasalamVariations[$key]);
160 }
161 }
162 }
163 }
164
165 return $this->finishUpdate($productId, $body, 'فرایند بروزرسانی �
166 حصول با �
167 وفقیت انجا�
168 شد.');
169 }
170
171 private function finishUpdate($productId, $body, string $message): array
172 {
173 update_post_meta($productId, ProductMetaKey::basalamProductSyncStatus(), 'synced');
174
175 $result = [
176 'success' => true,
177 'message' => $message,
178 'status_code' => 200,
179 ];
180
181 do_action('sync_basalam_after_update_product_api', $productId, $body, $result);
182
183 return $result;
184 }
185
186 private function shouldUpdateVariationsSeparately($productId, array $productData): bool
187 {
188 if (empty($productData['variants']) || !is_array($productData['variants'])) return false;
189
190 $product = \wc_get_product($productId);
191 if (!$product || !$product->is_type('variable')) return false;
192
193 return UpdateProductVariationsService::allVariantsHaveBasalamId($productData['variants']);
194 }
195
196 private function hasProductFieldsToUpdate(array $productData): bool
197 {
198 $identifiers = ['id' => true, 'type' => true];
199
200 return !empty(array_diff_key($productData, $identifiers));
201 }
202
203 private function stripForbiddenDescription(NonRetryableException $e, array &$productData, int $productId, int $attempt): bool
204 {
205 if (!isset($productData['description']) || !is_string($productData['description'])) return false;
206
207 $values = DescriptionErrorSanitizer::extractDescriptionValues($e->getResponseData());
208 if (empty($values)) return false;
209
210 $cleaned = DescriptionErrorSanitizer::sanitize($productData['description'], $values);
211 if ($cleaned === $productData['description']) return false;
212
213 $productData['description'] = $cleaned;
214
215 return true;
216 }
217
218 public function updateProductStatus($productId, $status)
219 {
220 $syncBasalamProductId = get_post_meta($productId, ProductMetaKey::basalamProductId(), true);
221
222 ProductConnection::assertUnique($productId, $syncBasalamProductId);
223
224 $url = sprintf(Endpoints::PRODUCT_UPDATE, $syncBasalamProductId);
225
226 $data = ["status" => $status];
227
228 $data = apply_filters('sync_basalam_product_status_data_before_update', $data, $productId, $status);
229
230 do_action('sync_basalam_before_update_product_status', $productId, $status, $data);
231
232 try {
233 $request = $this->apiservice->patch($url, $data);
234 } catch (RetryableException $e) {
235 throw $e;
236 } catch (NonRetryableException $e) {
237 throw $e;
238 } catch (\Exception $e) {
239 throw NonRetryableException::permanent(esc_html($e->getMessage()));
240 }
241
242 if (!is_wp_error($request)) {
243 update_post_meta($productId, ProductMetaKey::basalamProductSyncStatus(), 'synced');
244 update_post_meta($productId, ProductMetaKey::basalamProductStatus(), $status);
245
246 $result = [
247 'success' => true,
248 'message' => 'وضعیت �
249 حصول با �
250 وفقیت در باسلا�
251 تغییر کرد.',
252 'status_code' => 200,
253 ];
254
255 do_action('sync_basalam_after_update_product_status', $productId, $status, $result);
256
257 return $result;
258 }
259
260 throw NonRetryableException::permanent("تغییر وضعیت �
261 حصول در باسلا�
262 نا�
263 وفق بود.");
264 }
265 }
266