PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.19
ووسلام – همگام سازی ووکامرس و باسلام v1.10.19
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
← All changes | includes/Migrations/MigratorService.php +59 -1 1.9.11.10.19 View file →
@@ -2,8 +2,9 @@
2 2
3 3 namespace SyncBasalam\Migrations;
4 4
5 5 use SyncBasalam\Admin\Settings\SettingsConfig;
6 +use SyncBasalam\Services\Products\ProductConnection;
6 7 use SyncBasalam\Services\WebhookService;
7 8 use SyncBasalam\Utilities\ProductMetaKey;
8 9
9 10 defined('ABSPATH') || exit;
@@ -22,8 +23,9 @@
22 23 $source = $this->wpdb->prefix . 'bsl_payments';
23 24 $target = $this->wpdb->prefix . 'sync_basalam_payments';
24 25
25 26 if ($this->tableExists($source) && $this->tableExists($target)) {
27 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix / core $wpdb properties, not user input.
26 28 $this->wpdb->query("INSERT INTO $target (payment_id, invoice_id, user_id, city_id, province_id, order_id)
27 29 SELECT payment_id, invoice_id, user_id, city_id, province_id, order_id FROM $source");
28 30 }
29 31 }
@@ -33,8 +35,9 @@
33 35 $source = $this->wpdb->prefix . 'bsl_map_options';
34 36 $target = $this->wpdb->prefix . 'sync_basalam_map_options';
35 37
36 38 if ($this->tableExists($source) && $this->tableExists($target)) {
39 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix / core $wpdb properties, not user input.
37 40 $this->wpdb->query("INSERT INTO $target (woo_name, sync_basalam_name)
38 41 SELECT woo_name, bslm_name FROM $source");
39 42 }
40 43 }
@@ -44,8 +47,9 @@
44 47 $source = $this->wpdb->prefix . 'bsl_uploaded_photo';
45 48 $target = $this->wpdb->prefix . 'sync_basalam_uploaded_media';
46 49
47 50 if ($this->tableExists($source) && $this->tableExists($target)) {
51 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix / core $wpdb properties, not user input.
48 52 $this->wpdb->query(
49 53 "INSERT IGNORE INTO {$target} (type, source_identity, media_id, media_url, created_at)
50 54 SELECT 'photo', CONCAT('attachment:', woo_photo_id), bslm_photo_id, bslm_photo_url, NOW()
51 55 FROM {$source}
@@ -55,8 +59,9 @@
55 59 }
56 60
57 61 public function migratePostMeta()
58 62 {
63 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix / core $wpdb properties, not user input.
59 64 $this->wpdb->query(
60 65 "UPDATE {$this->wpdb->postmeta}
61 66 SET meta_key = REPLACE(meta_key, 'bslm_', 'sync_basalam_')
62 67 WHERE meta_key LIKE '%bslm_%'"
@@ -61,8 +66,9 @@
61 66 SET meta_key = REPLACE(meta_key, 'bslm_', 'sync_basalam_')
62 67 WHERE meta_key LIKE '%bslm_%'"
63 68 );
64 69
70 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix / core $wpdb properties, not user input.
65 71 $this->wpdb->query(
66 72 "UPDATE {$this->wpdb->postmeta}
67 73 SET meta_key = REPLACE(meta_key, 'bsl_', 'sync_basalam_')
68 74 WHERE meta_key LIKE '%bsl_%'"
@@ -70,8 +76,9 @@
70 76 }
71 77
72 78 public function migrateOptionsRows()
73 79 {
80 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix / core $wpdb properties, not user input.
74 81 $this->wpdb->query(
75 82 "UPDATE {$this->wpdb->options}
76 83 SET option_name = REPLACE(option_name, 'bslm_', 'sync_basalam_')
77 84 WHERE option_name LIKE '%bslm_%'"
@@ -76,8 +83,9 @@
76 83 SET option_name = REPLACE(option_name, 'bslm_', 'sync_basalam_')
77 84 WHERE option_name LIKE '%bslm_%'"
78 85 );
79 86
87 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix / core $wpdb properties, not user input.
80 88 $this->wpdb->query(
81 89 "UPDATE {$this->wpdb->options}
82 90 SET option_name = REPLACE(option_name, 'bsl_', 'sync_basalam_')
83 91 WHERE option_name LIKE '%bsl_%'"
@@ -87,8 +95,9 @@
87 95 public function migrateActions()
88 96 {
89 97 $source = $this->wpdb->prefix . 'actionscheduler_actions';
90 98
99 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from $wpdb->prefix, not user input; values are prepared.
91 100 $this->wpdb->query(
92 101 $this->wpdb->prepare(
93 102 "UPDATE $source
94 103 SET hook = REPLACE(hook, 'bslm_', 'sync_basalam_')
@@ -97,8 +106,9 @@
97 106 'pending'
98 107 )
99 108 );
100 109
110 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from $wpdb->prefix, not user input; values are prepared.
101 111 $this->wpdb->query(
102 112 $this->wpdb->prepare(
103 113 "UPDATE $source
104 114 SET hook = REPLACE(hook, 'bsl_', 'sync_basalam_')
@@ -112,8 +122,9 @@
112 122 public function migrateSettings()
113 123 {
114 124 $settingsRowName = 'sync_basalam_settings';
115 125
126 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from core $wpdb property, not user input; value is prepared.
116 127 $settingsSerialized = $this->wpdb->get_var(
117 128 $this->wpdb->prepare(
118 129 "SELECT option_value FROM {$this->wpdb->options} WHERE option_name = %s",
119 130 $settingsRowName
@@ -132,9 +143,9 @@
132 143 'basalam_token' => 'token',
133 144 'basalam_refresh_token' => 'refresh_token',
134 145 'basalam_vendor_id' => 'vendor_id',
135 146 'basalam_is_vendor' => 'is_vendor',
136 - 'basalam_increase' => 'increase_price_value',
147 + 'basalam_increase' => 'price_change_value',
137 148 'basalam_round' => 'round_price',
138 149 'basalam_webhook_token' => 'webhook_header_token',
139 150 'basalam_auto_confirm_order' => 'auto_confirm_order',
140 151 'basalam_product_wholesale' => 'all_products_wholesale',
@@ -157,8 +168,9 @@
157 168 ];
158 169
159 170 foreach ($tables as $table) {
160 171 if ($this->tableExists($table)) {
172 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- One-time schema migration; table identifier from $wpdb->prefix, not user input.
161 173 $this->wpdb->query("RENAME TABLE $table TO {$table}_old");
162 174 }
163 175 }
164 176 }
@@ -165,8 +177,9 @@
165 177
166 178 private function tableExists($table)
167 179 {
168 180
181 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Legacy-data migration; no object cache for these one-time operational queries.
169 182 return $this->wpdb->get_var($this->wpdb->prepare("SHOW TABLES LIKE %s", $table)) === $table;
170 183 }
171 184
172 185 private function columnExists($table, $column)
@@ -171,8 +184,9 @@
171 184
172 185 private function columnExists($table, $column)
173 186 {
174 187
188 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from $wpdb->prefix, not user input; value is prepared.
175 189 $result = $this->wpdb->get_results($this->wpdb->prepare(
176 190 "SHOW COLUMNS FROM `$table` LIKE %s",
177 191 $column
178 192 ));
@@ -185,8 +199,9 @@
185 199
186 200 $table = $this->wpdb->prefix . 'sync_basalam_uploaded_photo';
187 201
188 202 if ($this->tableExists($table) && !$this->columnExists($table, 'created_at')) {
203 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- One-time schema migration; table identifier from $wpdb->prefix, not user input.
189 204 $this->wpdb->query("ALTER TABLE $table ADD COLUMN created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP");
190 205 }
191 206 }
192 207
@@ -211,8 +226,9 @@
211 226 ];
212 227
213 228 foreach ($columns as $columnName => $sql) {
214 229 if (!$this->columnExists($table, $columnName)) {
230 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- One-time schema migration; ALTER statement built from static SQL with a $wpdb->prefix table identifier, not user input.
215 231 $this->wpdb->query($sql);
216 232 }
217 233 }
218 234 }
@@ -224,8 +240,9 @@
224 240 if (!$this->tableExists($table)) {
225 241 return;
226 242 }
227 243
244 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Legacy-data migration; no object cache for these one-time operational queries; values are prepared.
228 245 $existingIndex = $this->wpdb->get_var(
229 246 $this->wpdb->prepare(
230 247 "SELECT INDEX_NAME FROM INFORMATION_SCHEMA.STATISTICS
231 248 WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s AND INDEX_NAME = %s LIMIT 1",
@@ -240,8 +257,9 @@
240 257
241 258 $this->removeDuplicateInvoicePayments($table);
242 259
243 260 $this->wpdb->hide_errors();
261 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- One-time schema migration; table identifier from $wpdb->prefix, not user input.
244 262 $this->wpdb->query("ALTER TABLE {$table} ADD UNIQUE KEY unique_invoice_id (invoice_id)");
245 263 $this->wpdb->show_errors();
246 264 }
247 265
@@ -246,8 +264,9 @@
246 264 }
247 265
248 266 private function removeDuplicateInvoicePayments($table)
249 267 {
268 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from $wpdb->prefix, not user input.
250 269 $duplicates = $this->wpdb->get_results(
251 270 "SELECT invoice_id, MIN(id) AS keep_id
252 271 FROM {$table}
253 272 WHERE invoice_id IS NOT NULL AND invoice_id > 0
@@ -262,8 +281,9 @@
262 281 foreach ($duplicates as $duplicate) {
263 282 $invoiceId = (int) $duplicate->invoice_id;
264 283 $keepId = (int) $duplicate->keep_id;
265 284
285 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from $wpdb->prefix, not user input; values are prepared.
266 286 $rowsToDelete = $this->wpdb->get_results(
267 287 $this->wpdb->prepare(
268 288 "SELECT id, order_id FROM {$table} WHERE invoice_id = %d AND id <> %d",
269 289 $invoiceId,
@@ -285,8 +305,9 @@
285 305 }
286 306 }
287 307 }
288 308
309 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Legacy-data migration; no object cache for these one-time operational queries.
289 310 $this->wpdb->delete($table, ['id' => (int) $row->id], ['%d']);
290 311 }
291 312
292 313 \SyncBasalam\Logger\Logger::debug(
@@ -298,14 +319,17 @@
298 319 public function addRetryAfterColumnToJobManager()
299 320 {
300 321 $tableName = $this->wpdb->prefix . 'sync_basalam_job_manager';
301 322
323 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from $wpdb->prefix, not user input; value is prepared.
302 324 $columnExists = $this->wpdb->get_var(
303 325 $this->wpdb->prepare("SHOW COLUMNS FROM `{$tableName}` LIKE %s", 'retry_after')
304 326 );
305 327
306 328 if (!$columnExists) {
329 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- One-time schema migration; table identifier from $wpdb->prefix, not user input.
307 330 $this->wpdb->query("ALTER TABLE {$tableName} ADD COLUMN retry_after BIGINT(20) NULL DEFAULT NULL");
331 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- One-time schema migration; table identifier from $wpdb->prefix, not user input.
308 332 $this->wpdb->query("ALTER TABLE {$tableName} ADD INDEX idx_retry_after (retry_after)");
309 333 }
310 334 }
311 335
@@ -329,8 +353,9 @@
329 353
330 354 foreach ($metaKeyMap as $oldMetaKey => $newMetaKey) {
331 355 if ($oldMetaKey === $newMetaKey) continue;
332 356
357 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from core $wpdb property, not user input; values are prepared.
333 358 $this->wpdb->query(
334 359 $this->wpdb->prepare(
335 360 "INSERT INTO {$postmetaTable} (post_id, meta_key, meta_value)
336 361 SELECT old_meta.post_id, %s, old_meta.meta_value
@@ -345,8 +370,9 @@
345 370 $oldMetaKey
346 371 )
347 372 );
348 373
374 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifier from core $wpdb property, not user input; value is prepared.
349 375 $this->wpdb->query(
350 376 $this->wpdb->prepare(
351 377 "DELETE FROM {$postmetaTable} WHERE meta_key = %s",
352 378 $oldMetaKey
@@ -365,8 +391,29 @@
365 391
366 392 update_option('sync_basalam_settings', $settings);
367 393 }
368 394
395 + /**
396 + * Variant price/stock used to follow the product price/stock checkboxes of the custom sync mode.
397 + * Copy the old values so variable products keep syncing after the new checkboxes are introduced.
398 + */
399 + public function backfillVariantSyncFields()
400 + {
401 + $settings = get_option('sync_basalam_settings', []);
402 + if (!is_array($settings)) return;
403 +
404 + // The new keys may already have been filled with their "0" default by a settings read.
405 + if (empty($settings[SettingsConfig::SYNC_PRODUCT_FIELD_VARIANT_PRICE])) {
406 + $settings[SettingsConfig::SYNC_PRODUCT_FIELD_VARIANT_PRICE] = $settings[SettingsConfig::SYNC_PRODUCT_FIELD_PRICE] ?? 0;
407 + }
408 +
409 + if (empty($settings[SettingsConfig::SYNC_PRODUCT_FIELD_VARIANT_STOCK])) {
410 + $settings[SettingsConfig::SYNC_PRODUCT_FIELD_VARIANT_STOCK] = $settings[SettingsConfig::SYNC_PRODUCT_FIELD_STOCK] ?? 0;
411 + }
412 +
413 + update_option('sync_basalam_settings', $settings);
414 + }
415 +
369 416 public function createUploadedMediaTable()
370 417 {
371 418 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
372 419
@@ -390,8 +437,17 @@
390 437
391 438 $this->migrateLegacyUploadedPhotos($tableName);
392 439 }
393 440
441 + /**
442 + * اصلاح محصولاتی که به دلیل کپی شدن، به یک محصول باسلام اتصال مشترک دارند.
443 + * قدیمی‌ترین محصول (محصول اصلی) متصل می‌ماند و اتصال نسخه‌های کپی حذف می‌شود.
444 + */
445 + public function repairDuplicateProductConnections(): void
446 + {
447 + ProductConnection::repairDuplicateConnections();
448 + }
449 +
394 450 private function migrateLegacyUploadedPhotos(string $targetTable): void
395 451 {
396 452 $legacyTable = $this->wpdb->prefix . 'sync_basalam_uploaded_photo';
397 453
@@ -396,8 +452,9 @@
396 452 $legacyTable = $this->wpdb->prefix . 'sync_basalam_uploaded_photo';
397 453
398 454 if (!$this->tableExists($legacyTable)) return;
399 455
456 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter -- Legacy-data migration; table identifiers from $wpdb->prefix, not user input.
400 457 $this->wpdb->query(
401 458 "INSERT IGNORE INTO {$targetTable} (type, source_identity, media_id, media_url, created_at)
402 459 SELECT 'photo', CONCAT('attachment:', woo_photo_id), sync_basalam_photo_id, sync_basalam_photo_url, COALESCE(created_at, NOW())
403 460 FROM {$legacyTable}
@@ -403,8 +460,9 @@
403 460 FROM {$legacyTable}
404 461 WHERE woo_photo_id IS NOT NULL"
405 462 );
406 463
464 + // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.SchemaChange, PluginCheck.Security.DirectDB.UnescapedDBParameter -- One-time schema migration; table identifier from $wpdb->prefix, not user input.
407 465 $this->wpdb->query("DROP TABLE IF EXISTS {$legacyTable}");
408 466 }
409 467
410 468 }