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