PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.9.1
ووسلام – همگام سازی ووکامرس و باسلام v1.9.1
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 / Migrations / MigratorService.php

MigratorService.php in ووسلام – همگام سازی ووکامرس و باسلام 1.9.1, at includes/Migrations/MigratorService.php

413 lines 13.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SyncBasalam\Migrations;
4
5 use SyncBasalam\Admin\Settings\SettingsConfig;
6 use SyncBasalam\Services\WebhookService;
7 use SyncBasalam\Utilities\ProductMetaKey;
8
9 defined('ABSPATH') || exit;
10 class MigratorService
11 {
12 private $wpdb;
13
14 public function __construct()
15 {
16 global $wpdb;
17 $this->wpdb = $wpdb;
18 }
19
20 public function migratePayments()
21 {
22 $source = $this->wpdb->prefix . 'bsl_payments';
23 $target = $this->wpdb->prefix . 'sync_basalam_payments';
24
25 if ($this->tableExists($source) && $this->tableExists($target)) {
26 $this->wpdb->query("INSERT INTO $target (payment_id, invoice_id, user_id, city_id, province_id, order_id)
27 SELECT payment_id, invoice_id, user_id, city_id, province_id, order_id FROM $source");
28 }
29 }
30
31 public function migrateOptions()
32 {
33 $source = $this->wpdb->prefix . 'bsl_map_options';
34 $target = $this->wpdb->prefix . 'sync_basalam_map_options';
35
36 if ($this->tableExists($source) && $this->tableExists($target)) {
37 $this->wpdb->query("INSERT INTO $target (woo_name, sync_basalam_name)
38 SELECT woo_name, bslm_name FROM $source");
39 }
40 }
41
42 public function migrateUploadedPhotos()
43 {
44 $source = $this->wpdb->prefix . 'bsl_uploaded_photo';
45 $target = $this->wpdb->prefix . 'sync_basalam_uploaded_media';
46
47 if ($this->tableExists($source) && $this->tableExists($target)) {
48 $this->wpdb->query(
49 "INSERT IGNORE INTO {$target} (type, source_identity, media_id, media_url, created_at)
50 SELECT 'photo', CONCAT('attachment:', woo_photo_id), bslm_photo_id, bslm_photo_url, NOW()
51 FROM {$source}
52 WHERE woo_photo_id IS NOT NULL"
53 );
54 }
55 }
56
57 public function migratePostMeta()
58 {
59 $this->wpdb->query(
60 "UPDATE {$this->wpdb->postmeta}
61 SET meta_key = REPLACE(meta_key, 'bslm_', 'sync_basalam_')
62 WHERE meta_key LIKE '%bslm_%'"
63 );
64
65 $this->wpdb->query(
66 "UPDATE {$this->wpdb->postmeta}
67 SET meta_key = REPLACE(meta_key, 'bsl_', 'sync_basalam_')
68 WHERE meta_key LIKE '%bsl_%'"
69 );
70 }
71
72 public function migrateOptionsRows()
73 {
74 $this->wpdb->query(
75 "UPDATE {$this->wpdb->options}
76 SET option_name = REPLACE(option_name, 'bslm_', 'sync_basalam_')
77 WHERE option_name LIKE '%bslm_%'"
78 );
79
80 $this->wpdb->query(
81 "UPDATE {$this->wpdb->options}
82 SET option_name = REPLACE(option_name, 'bsl_', 'sync_basalam_')
83 WHERE option_name LIKE '%bsl_%'"
84 );
85 }
86
87 public function migrateActions()
88 {
89 $source = $this->wpdb->prefix . 'actionscheduler_actions';
90
91 $this->wpdb->query(
92 $this->wpdb->prepare(
93 "UPDATE $source
94 SET hook = REPLACE(hook, 'bslm_', 'sync_basalam_')
95 WHERE hook LIKE %s AND status = %s",
96 '%bslm_%',
97 'pending'
98 )
99 );
100
101 $this->wpdb->query(
102 $this->wpdb->prepare(
103 "UPDATE $source
104 SET hook = REPLACE(hook, 'bsl_', 'sync_basalam_')
105 WHERE hook LIKE %s AND status = %s",
106 '%bsl_%',
107 'pending'
108 )
109 );
110 }
111
112 public function migrateSettings()
113 {
114 $settingsRowName = 'sync_basalam_settings';
115
116 $settingsSerialized = $this->wpdb->get_var(
117 $this->wpdb->prepare(
118 "SELECT option_value FROM {$this->wpdb->options} WHERE option_name = %s",
119 $settingsRowName
120 )
121 );
122
123 if (!$settingsSerialized) return;
124
125 $settings = maybe_unserialize($settingsSerialized);
126
127 if (!is_array($settings)) return;
128
129 $keyMap = [
130 'basalam_client_id' => 'client_id',
131 'basalam_webhook_id' => 'webhook_id',
132 'basalam_token' => 'token',
133 'basalam_refresh_token' => 'refresh_token',
134 'basalam_vendor_id' => 'vendor_id',
135 'basalam_is_vendor' => 'is_vendor',
136 'basalam_increase' => 'increase_price_value',
137 'basalam_round' => 'round_price',
138 'basalam_webhook_token' => 'webhook_header_token',
139 'basalam_auto_confirm_order' => 'auto_confirm_order',
140 'basalam_product_wholesale' => 'all_products_wholesale',
141 ];
142
143 $newSettings = [];
144 foreach ($settings as $key => $value) {
145 $newSettings[$keyMap[$key] ?? $key] = $value;
146 }
147
148 update_option($settingsRowName, $newSettings);
149 }
150
151 public function renameOldTables()
152 {
153 $tables = [
154 $this->wpdb->prefix . 'bsl_payments',
155 $this->wpdb->prefix . 'bsl_map_options',
156 $this->wpdb->prefix . 'bsl_uploaded_photo',
157 ];
158
159 foreach ($tables as $table) {
160 if ($this->tableExists($table)) {
161 $this->wpdb->query("RENAME TABLE $table TO {$table}_old");
162 }
163 }
164 }
165
166 private function tableExists($table)
167 {
168
169 return $this->wpdb->get_var($this->wpdb->prepare("SHOW TABLES LIKE %s", $table)) === $table;
170 }
171
172 private function columnExists($table, $column)
173 {
174
175 $result = $this->wpdb->get_results($this->wpdb->prepare(
176 "SHOW COLUMNS FROM `$table` LIKE %s",
177 $column
178 ));
179
180 return !empty($result);
181 }
182
183 public function addCreatedAtColumnToUploadedPhoto()
184 {
185
186 $table = $this->wpdb->prefix . 'sync_basalam_uploaded_photo';
187
188 if ($this->tableExists($table) && !$this->columnExists($table, 'created_at')) {
189 $this->wpdb->query("ALTER TABLE $table ADD COLUMN created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP");
190 }
191 }
192
193 public function addNewWebhookEvents()
194 {
195 $webhookService = new WebhookService();
196 $webhookService->setupWebhook();
197 }
198
199 public function addFinancialDataColumns()
200 {
201 $table = $this->wpdb->prefix . 'sync_basalam_payments';
202
203 if (!$this->tableExists($table)) {
204 return;
205 }
206
207 $columns = [
208 'fee_amount' => "ALTER TABLE $table ADD COLUMN fee_amount DECIMAL(10,2) DEFAULT 0",
209 'balance_amount' => "ALTER TABLE $table ADD COLUMN balance_amount DECIMAL(10,2) DEFAULT 0",
210 'purchase_count' => "ALTER TABLE $table ADD COLUMN purchase_count INT DEFAULT 0"
211 ];
212
213 foreach ($columns as $columnName => $sql) {
214 if (!$this->columnExists($table, $columnName)) {
215 $this->wpdb->query($sql);
216 }
217 }
218 }
219
220 public function addUniqueInvoiceIdIndex()
221 {
222 $table = $this->wpdb->prefix . 'sync_basalam_payments';
223
224 if (!$this->tableExists($table)) {
225 return;
226 }
227
228 $existingIndex = $this->wpdb->get_var(
229 $this->wpdb->prepare(
230 "SELECT INDEX_NAME FROM INFORMATION_SCHEMA.STATISTICS
231 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND INDEX_NAME = %s LIMIT 1",
232 $table,
233 'unique_invoice_id'
234 )
235 );
236
237 if ($existingIndex) {
238 return;
239 }
240
241 $this->removeDuplicateInvoicePayments($table);
242
243 $this->wpdb->hide_errors();
244 $this->wpdb->query("ALTER TABLE {$table} ADD UNIQUE KEY unique_invoice_id (invoice_id)");
245 $this->wpdb->show_errors();
246 }
247
248 private function removeDuplicateInvoicePayments($table)
249 {
250 $duplicates = $this->wpdb->get_results(
251 "SELECT invoice_id, MIN(id) AS keep_id
252 FROM {$table}
253 WHERE invoice_id IS NOT NULL AND invoice_id > 0
254 GROUP BY invoice_id
255 HAVING COUNT(*) > 1"
256 );
257
258 if (empty($duplicates)) {
259 return;
260 }
261
262 foreach ($duplicates as $duplicate) {
263 $invoiceId = (int) $duplicate->invoice_id;
264 $keepId = (int) $duplicate->keep_id;
265
266 $rowsToDelete = $this->wpdb->get_results(
267 $this->wpdb->prepare(
268 "SELECT id, order_id FROM {$table} WHERE invoice_id = %d AND id <> %d",
269 $invoiceId,
270 $keepId
271 )
272 );
273
274 foreach ($rowsToDelete as $row) {
275 $orderId = (int) ($row->order_id ?? 0);
276 if ($orderId > 0 && function_exists('wc_get_order')) {
277 $order = wc_get_order($orderId);
278 if ($order instanceof \WC_Order) {
279 try {
280 $order->delete(true);
281 } catch (\Throwable $e) {
282 \SyncBasalam\Logger\Logger::error(
283 "حذف سفارش تکراری ووکا�
284 رس با شناسه {$orderId} نا�
285 وفق بود: " . $e->getMessage()
286 );
287 }
288 }
289 }
290
291 $this->wpdb->delete($table, ['id' => (int) $row->id], ['%d']);
292 }
293
294 \SyncBasalam\Logger\Logger::debug(
295 "سفارشات تکراری برای invoice_id {$invoiceId} پاک‌سازی شدند، شناسه نگه‌داشته‌شده: {$keepId}"
296 );
297 }
298 }
299
300 public function addRetryAfterColumnToJobManager()
301 {
302 $tableName = $this->wpdb->prefix . 'sync_basalam_job_manager';
303
304 $columnExists = $this->wpdb->get_var(
305 $this->wpdb->prepare("SHOW COLUMNS FROM `{$tableName}` LIKE %s", 'retry_after')
306 );
307
308 if (!$columnExists) {
309 $this->wpdb->query("ALTER TABLE {$tableName} ADD COLUMN retry_after BIGINT(20) NULL DEFAULT NULL");
310 $this->wpdb->query("ALTER TABLE {$tableName} ADD INDEX idx_retry_after (retry_after)");
311 }
312 }
313
314 public function migrateProductMetaKeysToVendorId()
315 {
316 $settings = get_option('sync_basalam_settings', []);
317 if (!is_array($settings)) return;
318
319 $vendorId = $settings[SettingsConfig::VENDOR_ID] ?? null;
320 $vendorId = preg_replace('/[^a-zA-Z0-9_-]/', '', (string) $vendorId);
321
322 if ($vendorId === '') return;
323
324 $postmetaTable = $this->wpdb->postmeta;
325
326 $metaKeyMap = [
327 ProductMetaKey::PRODUCT_ID => ProductMetaKey::basalamProductId($vendorId),
328 ProductMetaKey::PRODUCT_SYNC_STATUS => ProductMetaKey::basalamProductSyncStatus($vendorId),
329 ProductMetaKey::PRODUCT_STATUS => ProductMetaKey::basalamProductStatus($vendorId),
330 ];
331
332 foreach ($metaKeyMap as $oldMetaKey => $newMetaKey) {
333 if ($oldMetaKey === $newMetaKey) continue;
334
335 $this->wpdb->query(
336 $this->wpdb->prepare(
337 "INSERT INTO {$postmetaTable} (post_id, meta_key, meta_value)
338 SELECT old_meta.post_id, %s, old_meta.meta_value
339 FROM {$postmetaTable} old_meta
340 LEFT JOIN {$postmetaTable} new_meta
341 ON new_meta.post_id = old_meta.post_id
342 AND new_meta.meta_key = %s
343 WHERE old_meta.meta_key = %s
344 AND new_meta.meta_id IS NULL",
345 $newMetaKey,
346 $newMetaKey,
347 $oldMetaKey
348 )
349 );
350
351 $this->wpdb->query(
352 $this->wpdb->prepare(
353 "DELETE FROM {$postmetaTable} WHERE meta_key = %s",
354 $oldMetaKey
355 )
356 );
357 }
358 }
359
360 public function clearAuthTokens()
361 {
362 $settings = get_option('sync_basalam_settings', []);
363 if (!is_array($settings)) return;
364
365 $settings[SettingsConfig::TOKEN] = null;
366 $settings[SettingsConfig::REFRESH_TOKEN] = null;
367
368 update_option('sync_basalam_settings', $settings);
369 }
370
371 public function createUploadedMediaTable()
372 {
373 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
374
375 $tableName = $this->wpdb->prefix . 'sync_basalam_uploaded_media';
376 $charsetCollate = $this->wpdb->get_charset_collate();
377
378 $sql = "CREATE TABLE $tableName (
379 id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
380 type VARCHAR(16) NOT NULL,
381 source_identity VARCHAR(191) NOT NULL,
382 media_id BIGINT UNSIGNED NULL,
383 media_url VARCHAR(2083) NULL,
384 created_at DATETIME NOT NULL,
385 PRIMARY KEY (id),
386 UNIQUE KEY unique_type_source (type, source_identity),
387 KEY idx_type_created (type, created_at),
388 KEY idx_created_at (created_at)
389 ) $charsetCollate;";
390
391 dbDelta($sql);
392
393 $this->migrateLegacyUploadedPhotos($tableName);
394 }
395
396 private function migrateLegacyUploadedPhotos(string $targetTable): void
397 {
398 $legacyTable = $this->wpdb->prefix . 'sync_basalam_uploaded_photo';
399
400 if (!$this->tableExists($legacyTable)) return;
401
402 $this->wpdb->query(
403 "INSERT IGNORE INTO {$targetTable} (type, source_identity, media_id, media_url, created_at)
404 SELECT 'photo', CONCAT('attachment:', woo_photo_id), sync_basalam_photo_id, sync_basalam_photo_url, COALESCE(created_at, NOW())
405 FROM {$legacyTable}
406 WHERE woo_photo_id IS NOT NULL"
407 );
408
409 $this->wpdb->query("DROP TABLE IF EXISTS {$legacyTable}");
410 }
411
412 }
413