Dodano pomiar czasu wykonania skryptu importu produktów oraz optymalizacja logiki synchronizacji i aktualizacji produktów w PrestaShop.
This commit is contained in:
@@ -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 "<p>Wyłączono produkty niedostępne w feedzie.</p>";
|
||||
@@ -215,7 +232,9 @@ if ($modeSynch) {
|
||||
|
||||
// Posprzątaj
|
||||
$db->execute('DROP TABLE `'._DB_PREFIX_.'sollux_temp`');
|
||||
echo '<p>Zakończono synchronizację.</p>';
|
||||
|
||||
$totalTime = microtime(true) - $scriptStartTime;
|
||||
echo '<p>Zakończono synchronizację. Całkowity czas: '.formatTime($totalTime).'</p>';
|
||||
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 "<p>Zaktualizowano: $sku (Cena netto: $priceNet)</p>";
|
||||
$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 '<div style="background:#f0f0f0; border:1px solid #ccc; padding:10px; margin:10px 0;">';
|
||||
echo '<strong>Raport czasu (Update):</strong><br>';
|
||||
echo 'Szukanie w bazie: ' . formatTime($tCheckDuration) . '<br>';
|
||||
echo 'Zapis update (SQL): ' . formatTime($tUpdateDuration) . '<br>';
|
||||
echo '</div>';
|
||||
|
||||
$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 . '<br><hr>' . $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 "<p style='color:green'>Dodano produkt: " . $product->name[Context::getContext()->language->id] . " ($sku)</p>";
|
||||
|
||||
// --- RAPORT WYDAJNOŚCI ---
|
||||
$totalProductTime = microtime(true) - $tStartCheck; // Czas od sprawdzenia do końca zdjęć
|
||||
|
||||
echo '<div style="border: 2px solid #333; padding: 15px; background: #fff; margin: 15px 0; font-family: monospace;">';
|
||||
echo '<h3 style="margin-top:0;">⏱️ Raport czasu dla: '.$sku.'</h3>';
|
||||
echo '<ul>';
|
||||
echo '<li><strong>Wczytanie XML (cały plik):</strong> '.formatTime($tXmlDuration).'</li>';
|
||||
echo '<li><strong>Szukanie w bazie (czy istnieje):</strong> '.formatTime($tCheckDuration).'</li>';
|
||||
echo '<li><strong>Przygotowanie danych (obiekt):</strong> '.formatTime($tPrepDuration).'</li>';
|
||||
echo '<li><strong>Zapis produktu do SQL (INSERT):</strong> '.formatTime($tSaveDuration).'</li>';
|
||||
echo '<li><strong>ZDJĘCIA ('.$imgCount.' szt.) - Pobranie + Resize:</strong> <strong style="color:red">'.formatTime($tImagesDuration).'</strong></li>';
|
||||
echo '<li>----------------------------------</li>';
|
||||
echo '<li><strong>ŁĄCZNIE DLA TEGO PRODUKTU:</strong> <strong>'.formatTime($totalProductTime).'</strong></li>';
|
||||
echo '</ul>';
|
||||
echo '</div>';
|
||||
// --------------------------
|
||||
|
||||
$addedCount++;
|
||||
|
||||
// Break po dodaniu jednego, aby odciążyć serwer (skrypt musi być wywoływany cyklicznie)
|
||||
// Break po dodaniu jednego
|
||||
break;
|
||||
} else {
|
||||
echo "<p style='color:red'>Błąd podczas dodawania produktu: $sku</p>";
|
||||
|
||||
Reference in New Issue
Block a user