prefix . 'fct_product_details'; $variationsTable = $wpdb->prefix . 'fct_product_variations'; $lastId = 0; $taxClassSlugMap = []; do { // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $rows = $wpdb->get_results($wpdb->prepare( "SELECT `id`, `post_id`, `other_info` FROM `{$detailsTable}` WHERE `id` > %d ORDER BY `id` ASC LIMIT %d", $lastId, static::$chunkSize )); if (empty($rows)) { break; } $qualifyingRows = []; $postIds = []; foreach ($rows as $row) { $lastId = (int) $row->id; $detailInfo = json_decode($row->other_info, true); if (!is_array($detailInfo)) { continue; } $productTaxExempt = isset($detailInfo['tax_exempt']) ? (string) $detailInfo['tax_exempt'] : ''; $productTaxClass = isset($detailInfo['tax_class']) ? (string) $detailInfo['tax_class'] : ''; // Resolve tax_class stored as a numeric ID (product level) to its slug (variation level). $taxClassSlug = static::resolveVariationTaxClassSlug($productTaxClass, $taxClassSlugMap); $needsExempt = ($productTaxExempt === 'yes'); $needsClass = ($taxClassSlug !== ''); if (!$needsExempt && !$needsClass) { continue; } $postId = (int) $row->post_id; $qualifyingRows[] = [ 'post_id' => $postId, 'needs_exempt' => $needsExempt, 'tax_class' => $taxClassSlug, ]; $postIds[$postId] = $postId; } if (empty($qualifyingRows)) { continue; } $variationGroups = []; $variationInfoMap = []; $placeholders = implode(', ', array_fill(0, count($postIds), '%d')); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $variations = $wpdb->get_results($wpdb->prepare( "SELECT `id`, `post_id`, `other_info` FROM `{$variationsTable}` WHERE `post_id` IN ({$placeholders})", array_values($postIds) )); foreach ($variations as $variation) { $variationGroups[(int) $variation->post_id][] = $variation; } foreach ($qualifyingRows as $qualifyingRow) { $variations = $variationGroups[$qualifyingRow['post_id']] ?? []; foreach ($variations as $variation) { $variationId = (int) $variation->id; if (!array_key_exists($variationId, $variationInfoMap)) { $varInfo = !empty($variation->other_info) ? json_decode($variation->other_info, true) : []; if (!is_array($varInfo)) { $varInfo = []; } $variationInfoMap[$variationId] = $varInfo; } $varInfo = $variationInfoMap[$variationId]; $updated = false; // Only set tax_exempt when the variation has no explicit value at all. if ($qualifyingRow['needs_exempt'] && !array_key_exists('tax_exempt', $varInfo)) { $varInfo['tax_exempt'] = 'yes'; $updated = true; } // Only set tax_class when the variation has no explicit value at all. if ($qualifyingRow['tax_class'] !== '' && !array_key_exists('tax_class', $varInfo)) { $varInfo['tax_class'] = $qualifyingRow['tax_class']; $updated = true; } if (!$updated) { continue; } $variationInfoMap[$variationId] = $varInfo; $wpdb->update( $variationsTable, ['other_info' => wp_json_encode($varInfo)], ['id' => $variationId], ['%s'], ['%d'] ); } } } while (count($rows) === static::$chunkSize); update_option(static::$taxBackfillCompletionOption, 'yes', 'no'); } protected static function resolveVariationTaxClassSlug($productTaxClass, array &$taxClassSlugMap): string { if ($productTaxClass === '') { return ''; } if (is_numeric($productTaxClass)) { $taxClassId = (int) $productTaxClass; if (!array_key_exists($taxClassId, $taxClassSlugMap)) { global $wpdb; $taxClassTable = $wpdb->prefix . 'fct_tax_classes'; // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $slug = $wpdb->get_var($wpdb->prepare( "SELECT `slug` FROM `{$taxClassTable}` WHERE `id` = %d LIMIT 1", $taxClassId )); $taxClassSlugMap[$taxClassId] = $slug ? sanitize_key((string) $slug) : ''; } $productTaxClass = $taxClassSlugMap[$taxClassId]; } else { $productTaxClass = sanitize_key($productTaxClass); } // 'standard' is already the variation default — nothing to backfill. if ($productTaxClass === 'standard') { return ''; } return $productTaxClass; } }