/**
* Optimized BTS Stock & Price Sync
*
* CHANGES FROM ORIGINAL:
* 1. Memory management: Cache flush and garbage collection every 500 products
* 2. Chunking: Process products in batches instead of all at once
* 3. Optimized queries: Single JOIN query to fetch all meta data at once
* 4. Verbose logging: Memory usage and detailed progress reporting
* 5. Configurable chunk size via --chunk-size parameter
*
* @param bool $dry_run
* @param bool $safe_mode
* @param bool $zero_missing
* @param int $chunk_size Number of products to process per chunk (default: 500)
* @return array
*/
function master_sync_bts_optimized($dry_run = false, $safe_mode = false, $zero_missing = false, $chunk_size = 500) {
WP_CLI::log('');
WP_CLI::log('๐ฆ BTS WHOLESALER SYNC (OPTIMIZED)');
WP_CLI::log('=================================');
$start_time = microtime(true);
$start_memory = memory_get_usage(true);
try {
require_once __DIR__ . '/../api/class-parfumselect-bts-api.php';
$bts_api = new Parfumselect_BTS_API();
// STEP 1: Fetch API products
WP_CLI::log('๐ฅ Fetching products from BTS API...');
$api_products = $bts_api->getProducts();
if (empty($api_products)) {
throw new Exception("Geen producten ontvangen van BTS API");
}
WP_CLI::log('โ
Received ' . number_format(count($api_products)) . ' products from BTS API');
// STEP 2: Build EAN map
WP_CLI::log('๐๏ธ Building EAN map...');
$ean_map = [];
foreach ($api_products as $product) {
$ean = trim($product['ean'] ?? '');
if (!empty($ean)) {
$ean_map[$ean] = $product;
}
}
WP_CLI::log('โ
EAN map built with ' . number_format(count($ean_map)) . ' entries');
// Free up memory from API products array
unset($api_products);
gc_collect_cycles();
// STEP 3: Fetch products with EAN using optimized JOIN query
WP_CLI::log('๐ Fetching local products with EAN...');
global $wpdb;
$local_products = $wpdb->get_results(
"SELECT DISTINCT
p.ID as post_id,
ean.meta_value as ean,
bts_stock.meta_value as current_bts_stock,
bts_cost.meta_value as current_bts_cost
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} ean ON (
p.ID = ean.post_id
AND ean.meta_key = '_global_unique_id'
AND ean.meta_value != ''
)
LEFT JOIN {$wpdb->postmeta} bts_stock ON (
p.ID = bts_stock.post_id
AND bts_stock.meta_key = '_supplier_bts_stock'
)
LEFT JOIN {$wpdb->postmeta} bts_cost ON (
p.ID = bts_cost.post_id
AND bts_cost.meta_key = '_supplier_bts_cost'
)
WHERE p.post_type = 'product'
AND p.post_status = 'publish'",
OBJECT_K
);
WP_CLI::log('โ
Found ' . number_format(count($local_products)) . ' local products with EAN');
$results = [
'updated' => 0,
'skipped' => 0,
'unchanged' => 0,
];
// STEP 4: Process in chunks
WP_CLI::log('');
WP_CLI::log('๐ Processing products in chunks of ' . number_format($chunk_size));
WP_CLI::log(' Memory limit: ' . ini_get('memory_limit'));
WP_CLI::log(' Start memory: ' . size_format(memory_get_usage(true)));
WP_CLI::log('');
$product_ids = array_keys($local_products);
$total_products = count($product_ids);
$chunks = array_chunk($product_ids, $chunk_size);
$total_chunks = count($chunks);
foreach ($chunks as $chunk_index => $chunk_product_ids) {
$chunk_num = $chunk_index + 1;
$chunk_start = $chunk_index * $chunk_size + 1;
$chunk_end = min($chunk_num * $chunk_size, $total_products);
WP_CLI::log("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
WP_CLI::log("๐ฆ Processing Chunk {$chunk_num}/{$total_chunks} (products {$chunk_start}-{$chunk_end})");
WP_CLI::log(" Memory before: " . size_format(memory_get_usage(true)));
$chunk_updated = 0;
$chunk_unchanged = 0;
$chunk_skipped = 0;
foreach ($chunk_product_ids as $product_id) {
if (!isset($local_products[$product_id])) {
$chunk_skipped++;
continue;
}
$product_data = $local_products[$product_id];
$ean = trim($product_data->ean);
if (empty($ean) || !isset($ean_map[$ean])) {
$chunk_skipped++;
continue;
}
$api_product = $ean_map[$ean];
// Clean price string (e.g., "13.31โฌ" -> 13.31)
$price_string = $api_product['price'] ?? '0';
$cleaned_string = preg_replace('/[^0-9,.]/', '', $price_string);
$cleaned_string = str_replace(',', '.', $cleaned_string);
$new_cost = (float) $cleaned_string;
$new_stock = (int) ($api_product['stock'] ?? 0);
$current_bts_stock = (int) ($product_data->current_bts_stock ?? 0);
$current_bts_cost = (float) ($product_data->current_bts_cost ?? 0);
if ($current_bts_stock !== $new_stock || $current_bts_cost !== $new_cost) {
if (!$dry_run) {
update_post_meta($product_id, '_supplier_bts_stock', $new_stock);
update_post_meta($product_id, '_supplier_bts_cost', $new_cost);
update_post_meta($product_id, '_supplier_bts_id', $ean);
if (function_exists('calculate_best_supplier')) {
calculate_best_supplier($product_id, $safe_mode);
}
wc_delete_product_transients($product_id);
}
$chunk_updated++;
} else {
$chunk_unchanged++;
}
}
$results['updated'] += $chunk_updated;
$results['unchanged'] += $chunk_unchanged;
$results['skipped'] += $chunk_skipped;
// MEMORY MANAGEMENT: Clear cache and run garbage collection
wp_cache_flush();
if (function_exists('gc_collect_cycles')) {
gc_collect_cycles();
}
$peak_memory = memory_get_usage(true);
WP_CLI::log(" Updated: {$chunk_updated} | Unchanged: {$chunk_unchanged} | Skipped: {$chunk_skipped}");
WP_CLI::log(" Memory after: " . size_format($peak_memory));
WP_CLI::log(" Peak memory: " . size_format(memory_get_peak_usage(true)));
}
WP_CLI::log("โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
WP_CLI::log('');
// STEP 5: Zero missing products (if enabled)
if ($zero_missing && !$dry_run) {
WP_CLI::log('๐ Controleren op ontbrekende producten in BTS API...');
$zeroed_count = 0;
// Get all products with BTS ID (not just those with EAN)
$bts_products = $wpdb->get_results(
"SELECT p.ID as post_id,
bts_id.meta_value as bts_id,
bts_stock.meta_value as current_stock
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->postmeta} bts_id ON (p.ID = bts_id.post_id AND bts_id.meta_key = '_supplier_bts_id' AND bts_id.meta_value != '')
LEFT JOIN {$wpdb->postmeta} bts_stock ON (p.ID = bts_stock.post_id AND bts_stock.meta_key = '_supplier_bts_stock')
WHERE p.post_type = 'product'
AND p.post_status = 'publish'",
OBJECT_K
);
WP_CLI::log(' Found ' . count($bts_products) . ' products with BTS ID');
foreach ($bts_products as $product_data) {
$ean = trim($product_data->bts_id); // BTS ID is the EAN
// Check if this EAN is NOT in API
if (!isset($ean_map[$ean])) {
$product_id = $product_data->post_id;
$current_bts_stock = (int) ($product_data->current_stock ?? 0);
// Only zero out if current stock > 0 (avoid unnecessary updates)
if ($current_bts_stock > 0) {
// Update BTS supplier stock to 0
update_post_meta($product_id, '_supplier_bts_stock', 0);
// Use calculate_best_supplier to handle full price/stock recalculation
if (function_exists('calculate_best_supplier')) {
// Always use save() for zero-missing (safe mode)
calculate_best_supplier($product_id, true);
}
wc_delete_product_transients($product_id);
$zeroed_count++;
}
}
}
if ($zeroed_count > 0) {
WP_CLI::log("โ ๏ธ {$zeroed_count} producten niet gevonden in API โ Supplier stock op 0 gezet");
} else {
WP_CLI::log("โ
Alle producten gevonden in API");
}
// Add to results
$results['zeroed_missing'] = $zeroed_count;
// Memory cleanup
wp_cache_flush();
if (function_exists('gc_collect_cycles')) {
gc_collect_cycles();
}
}
$duration = round(microtime(true) - $start_time, 2);
$end_memory = memory_get_usage(true);
$memory_used = $end_memory - $start_memory;
WP_CLI::log('');
WP_CLI::log("โ
BTS sync voltooid in {$duration}s");
WP_CLI::log('๐ Memory used: ' . size_format($memory_used));
WP_CLI::log('๐ Peak memory: ' . size_format(memory_get_peak_usage(true)));
$log_message = " Bijgewerkt: {$results['updated']} | Ongewijzigd: {$results['unchanged']} | Overgeslagen: {$results['skipped']}";
if ($zero_missing && isset($results['zeroed_missing'])) {
$log_message .= " | Nieuw op 0: {$results['zeroed_missing']}";
}
WP_CLI::log($log_message);
return [
'success' => true,
'duration' => $duration,
'results' => $results,
'memory_used' => $memory_used,
'peak_memory' => memory_get_peak_usage(true),
];
} catch (Exception $e) {
WP_CLI::warning("โ ๏ธ BTS sync mislukt: " . $e->getMessage());
return [
'success' => false,
'error' => $e->getMessage(),
'results' => [
'updated' => 0,
'skipped' => 0,
'unchanged' => 0,
'zeroed_missing' => 0,
]
];
}
}
Eau de Parfum - Parfumselect
Gratis verzending vanaf โฌ80,-
“TRUSSARDI PURE JASMINE eau de parfum spray 30 ml” is toegevoegd aan je winkelwagen.
Bekijk winkelwagen
Registreren
Aanmelden voor vroege toegang Verkoop plus op maat van de nieuwkomers, trends en promoties. Om opt-out, klikt u op afmelden in onze e-mails.
34.94%
Nog € 52,05 tot GRATIS VERZENDING
Free Shipping Bar Attributes