From 45b32a5ee20453b6c92fdfe7995b0a7855de9df1 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Sun, 21 Dec 2025 15:07:29 +0100 Subject: [PATCH] =?UTF-8?q?Dodano=20pomiar=20czasu=20wykonania=20skryptu?= =?UTF-8?q?=20importu=20produkt=C3=B3w=20oraz=20optymalizacja=20logiki=20s?= =?UTF-8?q?ynchronizacji=20i=20aktualizacji=20produkt=C3=B3w=20w=20PrestaS?= =?UTF-8?q?hop.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- import-products.php | 145 ++++++++++++++++++++++++++++++-------------- 1 file changed, 99 insertions(+), 46 deletions(-) diff --git a/import-products.php b/import-products.php index d9c8c51b..884826e1 100644 --- a/import-products.php +++ b/import-products.php @@ -2,6 +2,9 @@ // URL do pliku XML: // https://sollux-lighting.com/product_feed/sollux/XML_polski_PLN_wszystkie_vip.xml +// START POMIARU CAŁEGO SKRYPTU +$scriptStartTime = microtime(true); + // Załaduj konfigurację PrestaShop include(dirname(__FILE__).'/config/config.inc.php'); include(dirname(__FILE__).'/init.php'); @@ -13,17 +16,24 @@ $context = Context::getContext(); // ============================================ // ID Producenta (Sollux Lighting) - podaj ID z PrestaShop -$configManufacturerId = 1; +$configManufacturerId = 198; -// ID Grupy Podatkowej (np. dla 23% VAT) - sprawdź w Lokalizacja > Podatki > Reguły podatkowe +// ID Grupy Podatkowej (np. dla 23% VAT) $configTaxRulesGroupId = 1; -// ID Kategorii domyślnej, do której mają trafić produkty (np. "Kinkiety" lub ogólna) -$configDefaultCategoryId = 12; +// ID Kategorii domyślnej, do której mają trafić produkty +$configDefaultCategoryId = 1072; // Czy włączyć produkt po imporcie? $configActive = 1; +// --- KONFIGURACJA CECHY (FEATURE) --- +// ID Cechy do przypisania (np. 20) +$configFeatureId = 20; + +// ID Wartości Cechy do przypisania (np. 19002) +$configFeatureValueId = 19002; + // ============================================ // Sprawdzenie trybu działania @@ -38,13 +48,21 @@ if (!$modeAdd && !$modeUpdate && !$modeSynch) { // Plik logu $logFile = __DIR__ . '/sollux_import_log.csv'; -// Wczytanie XML +// MIERZENIE: Ładowanie XML +$tXmlStart = microtime(true); $xmlUrl = 'https://sollux-lighting.com/product_feed/sollux/XML_polski_PLN_wszystkie_vip.xml'; $xml = simplexml_load_file($xmlUrl) or die("Error: Cannot create object from XML"); +$tXmlEnd = microtime(true); +$tXmlDuration = $tXmlEnd - $tXmlStart; // Czas parsowania XML // === FUNKCJE POMOCNICZE === +// Formatowanie czasu do wyświetlania +function formatTime($time) { + return number_format($time, 4) . ' s'; +} + // Tworzenie pola multilang (wymagane przez Prestę) function createMultiLangField($field) { $languages = Language::getLanguages(false); @@ -193,13 +211,12 @@ if ($modeSynch) { $db->execute('INSERT IGNORE INTO `'._DB_PREFIX_.'sollux_temp` (`sku`) VALUES '.implode(',', $sqlValues)); } - // Wyłącz produkty, których nie ma w XML a są w bazie (i są powiązane z tym producentem, opcjonalnie) - // Zakładamy, że sprawdzamy tylko produkty, które mają SKU. + // Wyłącz produkty, których nie ma w XML a są w bazie $sqlDisable = 'UPDATE `'._DB_PREFIX_.'product` p LEFT JOIN `'._DB_PREFIX_.'sollux_temp` t ON p.reference = t.sku SET p.active = 0 WHERE t.sku IS NULL - AND p.id_manufacturer = '.(int)$configManufacturerId; // Bezpiecznik: wyłączamy tylko produkty tego producenta + AND p.id_manufacturer = '.(int)$configManufacturerId; $db->execute($sqlDisable); echo "

Wyłączono produkty niedostępne w feedzie.

"; @@ -215,7 +232,9 @@ if ($modeSynch) { // Posprzątaj $db->execute('DROP TABLE `'._DB_PREFIX_.'sollux_temp`'); - echo '

Zakończono synchronizację.

'; + + $totalTime = microtime(true) - $scriptStartTime; + echo '

Zakończono synchronizację. Całkowity czas: '.formatTime($totalTime).'

'; exit; } @@ -225,12 +244,12 @@ if ($modeUpdate) { $today = date('Y-m-d'); $updatedCount = 0; - // Wczytaj log, aby nie aktualizować tego samego produktu wielokrotnie tego samego dnia + // Wczytaj log $processedSkus = []; if (file_exists($logFile)) { $lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); foreach ($lines as $line) { - $data = explode(';', $line); // Date;SKU + $data = explode(';', $line); if (isset($data[0]) && $data[0] == $today && isset($data[1])) { $processedSkus[trim($data[1])] = true; } @@ -243,39 +262,43 @@ if ($modeUpdate) { $sku = trim((string)$productNode->sku); if (empty($sku)) continue; - // Jeśli już zrobiony dzisiaj - pomiń if (isset($processedSkus[$sku])) continue; + // MIERZENIE: Szukanie produktu + $tStartCheck = microtime(true); $product = findProductByReference($sku); + $tCheckDuration = microtime(true) - $tStartCheck; + if (!$product) { - // Produkt nie istnieje w bazie - w trybie update pomijamy continue; } - // --- Aktualizacja Ceny --- + // MIERZENIE: Update produktu + $tStartUpdate = microtime(true); $priceGross = parsePrice($productNode->cena_detaliczna_brutto_pln); - $priceNet = Tools::ps_round($priceGross / 1.23, 6); // Zakładamy 23% VAT od brutto + $priceNet = Tools::ps_round($priceGross / 1.23, 6); $product->price = $priceNet; - $product->active = 1; // Przywracamy aktywność przy update + $product->active = 1; - // --- Aktualizacja Stanu (Domyślnie 100 jeśli jest w pliku) --- - // XML nie ma pola "qty", ale ma "paczkomat" i "orientacyjny czas". - // Zakładamy: jest w XML = dostępny. StockAvailable::setQuantity($product->id, 0, 100); $product->update(); + $tUpdateDuration = microtime(true) - $tStartUpdate; // Logowanie file_put_contents($logFile, $today.';'.$sku.PHP_EOL, FILE_APPEND); echo "

Zaktualizowano: $sku (Cena netto: $priceNet)

"; - $updatedCount++; - // Aktualizujemy 1 produkt na wywołanie, żeby nie przekroczyć czasu wykonywania? - // W poprzednim skrypcie był break. Jeśli masz crona co minutę, odkomentuj break. - // Jeśli odpalasz ręcznie w przeglądarce, break spowoduje konieczność ciągłego odświeżania. - // Zostawiam break dla bezpieczeństwa, odświeżaj stronę (meta reload na dole). + // RAPORT UPDATE + echo '
'; + echo 'Raport czasu (Update):
'; + echo 'Szukanie w bazie: ' . formatTime($tCheckDuration) . '
'; + echo 'Zapis update (SQL): ' . formatTime($tUpdateDuration) . '
'; + echo '
'; + + $updatedCount++; break; } @@ -296,41 +319,37 @@ if ($modeAdd) { $sku = trim((string)$productNode->sku); if (empty($sku)) continue; - // Sprawdź czy produkt już istnieje - if (findProductByReference($sku)) { - // Produkt istnieje, w trybie ADD nic nie robimy (ewentualnie można zrobić update) + // MIERZENIE: Sprawdzenie istnienia + $tStartCheck = microtime(true); + $exists = findProductByReference($sku); + $tCheckDuration = microtime(true) - $tStartCheck; + + if ($exists) { continue; } - // === TWORZENIE NOWEGO PRODUKTU === + // MIERZENIE: Przygotowanie obiektu (Pamięć) + $tStartPrep = microtime(true); $product = new Product(); $product->reference = $sku; $product->ean13 = trim((string)$productNode->ean); $product->name = createMultiLangField(trim((string)$productNode->nazwa_produktu)); - // Kategoria i Producent $product->id_category_default = (int)$configDefaultCategoryId; $product->id_manufacturer = (int)$configManufacturerId; - $product->id_tax_rules_group = (int)$configTaxRulesGroupId; // VAT + $product->id_tax_rules_group = (int)$configTaxRulesGroupId; - // Cena $priceGross = parsePrice($productNode->cena_detaliczna_brutto_pln); $product->price = Tools::ps_round($priceGross / 1.23, 6); - // Wymiary i Waga $product->width = (float)$productNode->szerokosc; $product->height = (float)$productNode->wysokosc; $product->depth = (float)$productNode->dlugosc; $product->weight = (float)$productNode->waga_produktu; - // Opisy $shortDesc = trim((string)$productNode->opis_krotki_html); $longDescRaw = trim((string)$productNode->opis_dlugi_korzysci_html); - - // Generowanie tabeli specyfikacji z atrybutów $attributesHtml = buildAttributesHtml($productNode->attributes); - - // Sklejenie opisu: Opis marketingowy + Tabela atrybutów $finalLongDesc = $longDescRaw . '

' . $attributesHtml; $product->description_short = createMultiLangField($shortDesc); @@ -338,29 +357,63 @@ if ($modeAdd) { $product->link_rewrite = createLinkRewrite((string)$productNode->nazwa_produktu); $product->active = (int)$configActive; + $tPrepDuration = microtime(true) - $tStartPrep; - // Dodanie do bazy - if ($product->add()) { - // Przypisanie do kategorii (domyślna + drzewo) + // MIERZENIE: Dodanie do bazy (INSERT) + $tStartSave = microtime(true); + $saveResult = $product->add(); + $tSaveDuration = microtime(true) - $tStartSave; + + if ($saveResult) { + // Przypisanie do kategorii i stanu $product->addToCategories([(int)$configDefaultCategoryId]); - - // Ustawienie stanu magazynowego (100) StockAvailable::setQuantity($product->id, 0, 100); - // === ZDJĘCIA === - // Loop przez image_1 do image_25 + // Dodanie Cechy + if (!empty($configFeatureId) && !empty($configFeatureValueId)) { + Product::addFeatureProductImport( + (int)$product->id, + (int)$configFeatureId, + (int)$configFeatureValueId + ); + } + + // MIERZENIE: Zdjęcia (Pobieranie + Resize) + $tStartImages = microtime(true); + $imgCount = 0; for ($i = 1; $i <= 25; $i++) { $imgTag = 'image_' . $i; $imgUrl = (string)$productNode->$imgTag; if (!empty($imgUrl)) { - addProductImage($product->id, $imgUrl); + if (addProductImage($product->id, $imgUrl)) { + $imgCount++; + } } } + $tImagesDuration = microtime(true) - $tStartImages; echo "

Dodano produkt: " . $product->name[Context::getContext()->language->id] . " ($sku)

"; + + // --- RAPORT WYDAJNOŚCI --- + $totalProductTime = microtime(true) - $tStartCheck; // Czas od sprawdzenia do końca zdjęć + + echo '
'; + echo '

⏱️ Raport czasu dla: '.$sku.'

'; + echo ''; + echo '
'; + // -------------------------- + $addedCount++; - // Break po dodaniu jednego, aby odciążyć serwer (skrypt musi być wywoływany cyklicznie) + // Break po dodaniu jednego break; } else { echo "

Błąd podczas dodawania produktu: $sku

";