id_product = (int)$productId;
$image->position = Image::getHighestPosition($productId) + 1;
$image->cover = ($image->position == 1); // Pierwsze zdjęcie jako okładka
if (!$image->add()) return false;
$imagePath = $image->getPathForCreation();
$url = str_replace(' ', '%20', trim($imageUrl));
// Próba pobrania
if (!@copy($url, $imagePath . '.jpg')) {
$image->delete();
return false;
}
// Generowanie miniatur
$imageTypes = ImageType::getImagesTypes('products');
foreach ($imageTypes as $imageType) {
if (!ImageManager::resize(
$imagePath . '.jpg',
$imagePath . '-' . stripslashes($imageType['name']) . '.jpg',
(int)$imageType['width'],
(int)$imageType['height']
)) {
// W razie błędu resize można obsłużyć wyjątek, tutaj cicho pomijamy
}
}
return true;
}
// Znajdź produkt po SKU (reference)
function findProductByReference($reference) {
if (empty($reference)) return false;
$sql = 'SELECT `id_product`
FROM `'._DB_PREFIX_.'product`
WHERE `reference` = \''.pSQL($reference).'\'';
$result = Db::getInstance()->getRow($sql);
return $result ? new Product((int)$result['id_product']) : false;
}
// Parsowanie ceny (zamiana przecinków na kropki, usuwanie spacji)
function parsePrice($rawPrice) {
$clean = str_replace([' ', ','], ['', '.'], (string)$rawPrice);
return (float)$clean;
}
// Budowanie tabeli HTML z atrybutów XML
function buildAttributesHtml($xmlAttributesNode) {
if (!$xmlAttributesNode || !isset($xmlAttributesNode->attribute)) {
return '';
}
$html = '
';
$html .= '
Specyfikacja produktu:
';
$html .= '
';
$html .= '';
foreach ($xmlAttributesNode->attribute as $attr) {
$name = (string)$attr->attribute_name;
$value = (string)$attr->attribute_value;
if (!empty($name) && !empty($value) && $value !== 'Nie dotyczy') {
$html .= '';
$html .= '| ' . htmlspecialchars($name) . ' | ';
$html .= '' . htmlspecialchars($value) . ' | ';
$html .= '
';
}
}
$html .= '';
$html .= '
';
$html .= '
';
return $html;
}
// Czyści log aktualizacji - zostawia X dni
function cleanLogFile($logFile, $days = 7) {
if (!file_exists($logFile)) return;
$content = file($logFile);
$newContent = [];
$cutoff = strtotime("-$days days");
foreach ($content as $line) {
$parts = explode(';', $line);
if (isset($parts[0]) && strtotime($parts[0]) >= $cutoff) {
$newContent[] = $line;
}
}
file_put_contents($logFile, implode("", $newContent));
}
// ============================================
// ================ LOGIKA ====================
// ============================================
// 1. TRYB SYNCHRONIZACJI (Włączanie/Wyłączanie produktów)
if ($modeSynch) {
echo 'Synchronizacja stanów (Active/Inactive)...
';
$db = Db::getInstance();
// Tabela tymczasowa na SKU z XML
$db->execute('CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'sollux_temp` (`sku` VARCHAR(64), PRIMARY KEY (`sku`)) ENGINE='._MYSQL_ENGINE_);
$db->execute('TRUNCATE TABLE `'._DB_PREFIX_.'sollux_temp`');
// Zbieranie SKU z XML
$sqlValues = [];
foreach ($xml->product as $productNode) {
$sku = trim((string)$productNode->sku);
if ($sku) {
$sqlValues[] = '(\''.pSQL($sku).'\')';
}
// Wstawiamy paczkami po 100, żeby nie zapchać SQL
if (count($sqlValues) >= 100) {
$db->execute('INSERT IGNORE INTO `'._DB_PREFIX_.'sollux_temp` (`sku`) VALUES '.implode(',', $sqlValues));
$sqlValues = [];
}
}
if (!empty($sqlValues)) {
$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
$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;
$db->execute($sqlDisable);
echo "Wyłączono produkty niedostępne w feedzie.
";
// Włącz produkty, które są w XML
$sqlEnable = 'UPDATE `'._DB_PREFIX_.'product` p
JOIN `'._DB_PREFIX_.'sollux_temp` t ON p.reference = t.sku
SET p.active = 1
WHERE p.active = 0';
$db->execute($sqlEnable);
echo "Włączono produkty dostępne w feedzie.
";
// Posprzątaj
$db->execute('DROP TABLE `'._DB_PREFIX_.'sollux_temp`');
$totalTime = microtime(true) - $scriptStartTime;
echo 'Zakończono synchronizację. Całkowity czas: '.formatTime($totalTime).'
';
exit;
}
// 2. TRYB AKTUALIZACJI (Ceny, Stany)
if ($modeUpdate) {
cleanLogFile($logFile);
$today = date('Y-m-d');
$updatedCount = 0;
// 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);
if (isset($data[0]) && $data[0] == $today && isset($data[1])) {
$processedSkus[trim($data[1])] = true;
}
}
}
echo "Rozpoczynam aktualizację...
";
foreach ($xml->product as $productNode) {
$sku = trim((string)$productNode->sku);
if (empty($sku)) continue;
if (isset($processedSkus[$sku])) continue;
// MIERZENIE: Szukanie produktu
$tStartCheck = microtime(true);
$product = findProductByReference($sku);
$tCheckDuration = microtime(true) - $tStartCheck;
if (!$product) {
continue;
}
// MIERZENIE: Update produktu
$tStartUpdate = microtime(true);
$priceGross = parsePrice($productNode->cena_detaliczna_brutto_pln);
$priceNet = Tools::ps_round($priceGross / 1.23, 6);
$product->price = $priceNet;
$product->active = 1;
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)
";
// RAPORT UPDATE
echo '';
echo 'Raport czasu (Update):
';
echo 'Szukanie w bazie: ' . formatTime($tCheckDuration) . '
';
echo 'Zapis update (SQL): ' . formatTime($tUpdateDuration) . '
';
echo '
';
$updatedCount++;
break;
}
if ($updatedCount > 0) {
echo '';
echo 'Trwa przeładowanie strony...
';
} else {
echo 'Wszystkie produkty z XML zostały już dzisiaj zaktualizowane.
';
}
exit;
}
// 3. TRYB DODAWANIA (ADD)
if ($modeAdd) {
$addedCount = 0;
foreach ($xml->product as $productNode) {
$sku = trim((string)$productNode->sku);
if (empty($sku)) continue;
// MIERZENIE: Sprawdzenie istnienia
$tStartCheck = microtime(true);
$exists = findProductByReference($sku);
$tCheckDuration = microtime(true) - $tStartCheck;
if ($exists) {
continue;
}
// 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));
$product->id_category_default = (int)$configDefaultCategoryId;
$product->id_manufacturer = (int)$configManufacturerId;
$product->id_tax_rules_group = (int)$configTaxRulesGroupId;
$priceGross = parsePrice($productNode->cena_detaliczna_brutto_pln);
$product->price = Tools::ps_round($priceGross / 1.23, 6);
$product->width = (float)$productNode->szerokosc;
$product->height = (float)$productNode->wysokosc;
$product->depth = (float)$productNode->dlugosc;
$product->weight = (float)$productNode->waga_produktu;
$shortDesc = trim((string)$productNode->opis_krotki_html);
$longDescRaw = trim((string)$productNode->opis_dlugi_korzysci_html);
$attributesHtml = buildAttributesHtml($productNode->attributes);
$finalLongDesc = $longDescRaw . '
' . $attributesHtml;
$product->description_short = createMultiLangField($shortDesc);
$product->description = createMultiLangField($finalLongDesc);
$product->link_rewrite = createLinkRewrite((string)$productNode->nazwa_produktu);
$product->active = (int)$configActive;
$tPrepDuration = microtime(true) - $tStartPrep;
// 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]);
StockAvailable::setQuantity($product->id, 0, 100);
// 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)) {
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 '- Wczytanie XML (cały plik): '.formatTime($tXmlDuration).'
';
echo '- Szukanie w bazie (czy istnieje): '.formatTime($tCheckDuration).'
';
echo '- Przygotowanie danych (obiekt): '.formatTime($tPrepDuration).'
';
echo '- Zapis produktu do SQL (INSERT): '.formatTime($tSaveDuration).'
';
echo '- ZDJĘCIA ('.$imgCount.' szt.) - Pobranie + Resize: '.formatTime($tImagesDuration).'
';
echo '- ----------------------------------
';
echo '- ŁĄCZNIE DLA TEGO PRODUKTU: '.formatTime($totalProductTime).'
';
echo '
';
echo '
';
// --------------------------
$addedCount++;
// Break po dodaniu jednego
break;
} else {
echo "Błąd podczas dodawania produktu: $sku
";
}
}
if ($addedCount > 0) {
echo '';
echo 'Dodano produkt. Odświeżanie...
';
} else {
echo 'Brak nowych produktów do dodania (lub wszystkie już istnieją).
';
}
}
?>