PluginProbe
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler / 1.6.5
FluentCart A New Era of eCommerce – Faster, Lighter, and Simpler v1.6.5
1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.4 1.5.5 1.5.3 1.5.2 1.5.1 1.5.0 1.4.2 1.4.1 1.4.0 1.3.28 1.3.27 1.3.26 1.3.25 1.3.23 1.3.22 1.3.21 1.3.20 1.3.19 All 49 releases
← All changes | database/DBMigrator.php +98 -9 1.4.2 → 1.6.5 View file →
@@ -6,11 +6,8 @@
6 6 use FluentCart\App\Models\Product;
7 7
8 8 require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
9 9
10 -use FluentCart\Database\Migrations\AttributeGroupsMigrator;
11 -use FluentCart\Database\Migrations\AttributeObjectRelationsMigrator;
12 -use FluentCart\Database\Migrations\AttributeTermsMigrator;
13 10 use FluentCart\Database\Migrations\CartMigrator;
14 11 use FluentCart\Database\Migrations\CustomersMigrator;
15 12 use FluentCart\Database\Migrations\MetaMigrator;
16 13 use FluentCart\Database\Migrations\Migrator;
@@ -47,16 +44,17 @@
47 44 use FluentCartPro\App\Modules\Licensing\Models\LicenseSite;
48 45 use FluentCart\Database\Migrations\ShippingZonesMigrator;
49 46 use FluentCart\Database\Migrations\ShippingMethodsMigrator;
50 47 use FluentCart\Database\Migrations\RetentionSnapshotsMigrator;
48 +use FluentCart\Database\Migrations\AttributeGroupsMigrator;
49 +use FluentCart\Database\Migrations\AttributeObjectRelationsMigrator;
50 +use FluentCart\Database\Migrations\AttributeTermsMigrator;
51 +use FluentCart\Database\Seeder\AttributeSeeder;
51 52
52 53 class DBMigrator
53 54 {
54 55 private static array $migrators = [
55 56 MetaMigrator::class,
56 - AttributeGroupsMigrator::class,
57 - AttributeObjectRelationsMigrator::class,
58 - AttributeTermsMigrator::class,
59 57 CartMigrator::class,
60 58 CouponsMigrator::class,
61 59 CustomerAddressesMigrator::class,
62 60 CustomerMetaMigrator::class,
@@ -85,9 +83,15 @@
85 83 ShippingZonesMigrator::class,
86 84 ShippingMethodsMigrator::class,
87 85 ShippingClassesMigrator::class,
88 86 ScheduledActionsMigrator::class,
89 - RetentionSnapshotsMigrator::class
87 + RetentionSnapshotsMigrator::class,
88 + // Order matters: AttributeTermsMigrator::migrated() INNER JOINs
89 + // fct_atts_relations to repoint orphaned term references, so the
90 + // relations table must exist before terms runs its post-migration hook.
91 + AttributeGroupsMigrator::class,
92 + AttributeObjectRelationsMigrator::class,
93 + AttributeTermsMigrator::class
90 94 ];
91 95
92 96 public static function migrateUp($network_wide = false)
93 97 {
@@ -125,12 +129,16 @@
125 129 */
126 130 foreach (self::$migrators as $migrator) {
127 131 $migrator::migrate();
128 132 }
133 +
134 + do_action('fluent_cart/after_migrate');
129 135 }
130 136
131 137 public static function maybeMigrateDBChanges()
132 138 {
139 + self::maybeMigrateCustomerRecoveryIndexes();
140 +
133 141 /*
134 142 * TODO We will remove this after final release
135 143 */
136 144 $currentDBVersion = get_option('_fluent_cart_db_version');
@@ -148,18 +156,34 @@
148 156 }
149 157
150 158 update_option('_fluent_cart_db_version', FLUENTCART_DB_VERSION, 'no');
151 159
160 + if (!$currentDBVersion) {
161 + // Brand-new store: default tax display to the simplified single line.
162 + // Existing stores fall through to the read-time default 'itemized'.
163 + $taxSettings = get_option('fluent_cart_tax_configuration_settings', []);
164 + if (!isset($taxSettings['checkout_tax_breakdown_display'])) {
165 + $taxSettings['checkout_tax_breakdown_display'] = 'simplified';
166 + update_option('fluent_cart_tax_configuration_settings', $taxSettings, true);
167 + }
168 + }
169 +
152 170 // 2026-04-25
153 171 TaxRatesMigrator::upgradeShippingOverridePrecision();
154 172
173 + // 2026-07-01
174 + OrderTaxRateMigrator::allowVirtualTaxRateIds();
175 +
155 176 // 2026-05-27
156 177 TaxRatesMigrator::fixPostcodeRangeSeparator();
157 178 OrdersMigrator::addFeeTotalColumn();
158 179
159 - // 2026-05-10
160 - AttributeGroupsMigrator::dropLegacyTitleUniqueIndexes();
180 + // 2026-07-24
181 + OrdersMigrator::addUuidIndex();
161 182
183 + // 2026-08-20
184 + ProductMetaMigrator::addObjectMetaIndex();
185 +
162 186 // let's check the orders table sequence number
163 187 global $wpdb;
164 188
165 189 // Product Meta unique index removal
@@ -461,9 +485,15 @@
461 485 \FluentCart\Database\Migrations\ShippingZonesMigrator::migrated();
462 486 \FluentCart\Database\Migrations\ShippingClassesMigrator::migrated();
463 487 \FluentCart\Database\Migrations\SubscriptionsMigrator::migrated();
464 488
489 + // 2026-08-06
490 + // fct_order_transactions subscription_id index — also run here so
491 + // in-place plugin updates deliver it; migrate() only covers the
492 + // activation path.
493 + \FluentCart\Database\Migrations\OrderTransactionsMigrator::migrated();
465 494
495 +
466 496 // 2026-05-06
467 497 // Backfill, dedup, then add unique index — must run in this order
468 498 // so the constraint can be applied without "Duplicate entry" errors.
469 499 TaxClassesMigrator::backfillNullSlugs();
@@ -497,8 +527,21 @@
497 527 }
498 528 update_option('_fluent_cart_price_suffix_migrated', '1', 'no');
499 529 }
500 530
531 + // 2026-07-08
532 + // One-time migration: rename the legacy tax-display value 'both'
533 + // (and the removed 'label'/'tooltip') to 'itemized'. Value-guarded —
534 + // once migrated the stored value is 'itemized' so this no-ops; no
535 + // separate flag option is written (nothing left behind when this
536 + // block is removed later). Safe to remove after ~2027-07.
537 + $fctTaxSettings = get_option('fluent_cart_tax_configuration_settings', []);
538 + if (isset($fctTaxSettings['checkout_tax_breakdown_display'])
539 + && in_array($fctTaxSettings['checkout_tax_breakdown_display'], ['both', 'label', 'tooltip'], true)) {
540 + $fctTaxSettings['checkout_tax_breakdown_display'] = 'itemized';
541 + update_option('fluent_cart_tax_configuration_settings', $fctTaxSettings, true);
542 + }
543 +
501 544 // 2026-05-18
502 545 // One-time migration: copy seller identity fields from the Pro seller_details fct_meta
503 546 // entry into fluent_cart_store_settings. Guarded by a sentinel so it runs exactly once.
504 547 if (!get_option('_fluent_cart_seller_details_migrated')) {
@@ -540,8 +583,54 @@
540 583 $table_name
541 584 ));
542 585 }
543 586
587 + // 2026-06-17
588 + // Advanced Variation - Create the attribute
589 + // schema (and seed the system-template groups) on in-place plugin
590 + // updates too — migrate() only runs on activation, but existing
591 + // installs reach this version-gated path without reactivating. Each
592 + // migrator is idempotent (dbDelta) and the seeder early-returns when
593 + // any group already exists, so re-runs never duplicate data. Order
594 + // matters: relations table must exist before AttributeTermsMigrator's
595 + // post-migration JOIN cleanup.
596 + AttributeGroupsMigrator::migrate();
597 + AttributeObjectRelationsMigrator::migrate();
598 + AttributeTermsMigrator::migrate();
599 + AttributeSeeder::seed();
600 +
601 + self::releaseMigrationLock();
602 + }
603 + }
604 +
605 + /** Upgrade active stores without running index DDL on customer requests. */
606 + public static function maybeMigrateCustomerRecoveryIndexes(): void
607 + {
608 + $option = '_fluent_cart_customer_recovery_indexes_version';
609 + if (get_option($option) === '1') {
610 + return;
611 + }
612 + if (!(defined('WP_CLI') && WP_CLI) && (!is_admin() || !current_user_can('manage_options'))) {
613 + return;
614 + }
615 + if (!self::acquireMigrationLock()) {
616 + return;
617 + }
618 + try {
619 + if (get_option($option) === '1') {
620 + return;
621 + }
622 + foreach ([SubscriptionsMigrator::class, OrderDownloadPermissionsMigrator::class, CartMigrator::class] as $migrator) {
623 + if (!Schema::hasTable($migrator::$tableName)) {
624 + return;
625 + }
626 + $migrator::addCustomerRecoveryIndex();
627 + if (!$migrator::hasCustomerRecoveryIndex()) {
628 + return; // Keep the upgrade pending if ALTER failed.
629 + }
630 + }
631 + update_option($option, '1', false);
632 + } finally {
544 633 self::releaseMigrationLock();
545 634 }
546 635 }
547 636