Add functionality for product update and addition from XML feed

- Implemented URL parameters to switch between add and update modes.
- Enhanced XML loading from a remote URL.
- Added logging for updated products with a daily log file.
- Improved product update logic to change default category if necessary.
- Refactored functions for better readability and performance.
- Ensured proper type casting for database interactions.
- Added functionality to create or update product combinations.
- Improved user feedback with HTML output for added products and combinations.
This commit is contained in:
2025-11-19 20:37:58 +01:00
parent 6ed7882f09
commit 4d03f9c793
2 changed files with 512 additions and 939 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,17 +1,37 @@
<?php
// https://pacyga.pl/wp-content/uploads/woo-feed/custom/xml/komplet-produktow-3.xml
// Include PrestaShop configuration
$config['update_price'] = false;
include(dirname(__FILE__).'/config/config.inc.php');
include(dirname(__FILE__).'/init.php');
// Load XML file
$xml = simplexml_load_file('komplet-produktow-3.xml') or die("Error: Cannot create object");
$context = Context::getContext();
// Sprawdzenie trybu działania na podstawie parametrów URL
$modeAdd = (Tools::getValue('add') === 'true');
$modeUpdate = (Tools::getValue('update') === 'true');
if (!$modeAdd && !$modeUpdate) {
die('Brak akcji. Dodaj do adresu ?add=true lub ?update=true');
}
// Plik logu aktualizacji cen
$logFile = __DIR__ . '/update_price_log.csv';
// Wczytanie XML
$xmlUrl = 'https://pacyga.pl/wp-content/uploads/woo-feed/custom/xml/komplet-produktow-3.xml';
$xml = simplexml_load_file($xmlUrl) or die("Error: Cannot create object");
// === FUNKCJE POMOCNICZE ===
// Function to find attribute group by name
function findAttributeGroupByName($name) {
$id_lang = Context::getContext()->language->id;
$result = Db::getInstance()->getRow('SELECT `id_attribute_group` FROM `'._DB_PREFIX_.'attribute_group_lang` WHERE `name` = \''.pSQL($name).'\' AND `id_lang` = '.(int)$id_lang);
$sql = 'SELECT `id_attribute_group`
FROM `'._DB_PREFIX_.'attribute_group_lang`
WHERE `name` = \''.pSQL($name).'\'
AND `id_lang` = '.(int)$id_lang;
$result = Db::getInstance()->getRow($sql);
return $result ? new AttributeGroup($result['id_attribute_group']) : false;
}
@@ -20,8 +40,11 @@ function findAttributeByName($id_attribute_group, $name) {
$id_lang = Context::getContext()->language->id;
$sql = 'SELECT a.`id_attribute`
FROM `'._DB_PREFIX_.'attribute` a
JOIN `'._DB_PREFIX_.'attribute_lang` al ON a.`id_attribute` = al.`id_attribute`
WHERE al.`name` = \''.pSQL($name).'\' AND al.`id_lang` = '.(int)$id_lang.' AND a.`id_attribute_group` = '.(int)$id_attribute_group;
JOIN `'._DB_PREFIX_.'attribute_lang` al
ON a.`id_attribute` = al.`id_attribute`
WHERE al.`name` = \''.pSQL($name).'\'
AND al.`id_lang` = '.(int)$id_lang.'
AND a.`id_attribute_group` = '.(int)$id_attribute_group;
$result = Db::getInstance()->getRow($sql);
return $result ? new Attribute($result['id_attribute']) : false;
}
@@ -55,7 +78,7 @@ function createMultiLangField($field) {
$languages = Language::getLanguages(false);
$res = [];
foreach ($languages as $lang) {
$res[$lang['id_lang']] = $field;
$res[(int)$lang['id_lang']] = $field;
}
return $res;
}
@@ -66,16 +89,16 @@ function createLinkRewrite($field) {
$res = [];
$linkRewrite = Tools::link_rewrite($field); // PrestaShop's function to create valid link_rewrite
foreach ($languages as $lang) {
$res[$lang['id_lang']] = $linkRewrite;
$res[(int)$lang['id_lang']] = $linkRewrite;
}
return $res;
}
// Function to get category ID from name
// Function to get category ID from name (nieużywana, ale poprawiona)
function getCategoryId($categoryName) {
$category = Category::searchByName(1, $categoryName); // 1 for default language id
if (!empty($category)) {
return $category['id_category'];
$result = Category::searchByName(1, $categoryName); // 1 for default language id
if (!empty($result) && isset($result[0]['id_category'])) {
return (int)$result[0]['id_category'];
} else {
// Create category if not exists
$category = new Category();
@@ -83,7 +106,7 @@ function getCategoryId($categoryName) {
$category->link_rewrite = createLinkRewrite($categoryName);
$category->id_parent = 2; // Default parent category
$category->add();
return $category->id;
return (int)$category->id;
}
}
@@ -91,7 +114,7 @@ function getCategoryId($categoryName) {
function addProductImage($productId, $imageUrl)
{
$image = new Image();
$image->id_product = $productId;
$image->id_product = (int)$productId;
$image->position = Image::getHighestPosition($productId) + 1;
$image->cover = true; // Set the first image as cover
$image->add();
@@ -105,7 +128,12 @@ function addProductImage($productId, $imageUrl)
$imageTypes = ImageType::getImagesTypes('products');
foreach ($imageTypes as $imageType) {
ImageManager::resize($imagePath . '.jpg', $imagePath . '-' . stripslashes($imageType['name']) . '.jpg', $imageType['width'], $imageType['height']);
ImageManager::resize(
$imagePath . '.jpg',
$imagePath . '-' . stripslashes($imageType['name']) . '.jpg',
(int)$imageType['width'],
(int)$imageType['height']
);
}
return true;
@@ -113,24 +141,38 @@ function addProductImage($productId, $imageUrl)
// Function to find product by reference
function findProductByReference($reference) {
$result = Db::getInstance()->getRow('SELECT `id_product` FROM `'._DB_PREFIX_.'product` WHERE `reference` = \''.pSQL($reference).'\'');
return $result ? new Product($result['id_product']) : 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;
}
// Function to find combination by product ID and attribute IDs
function findCombinationByAttributes($id_product, $attributeIds) {
if (empty($attributeIds)) {
return false;
}
sort($attributeIds);
$conditions = [];
foreach ($attributeIds as $id_attr) {
$conditions[] = 'pac.`id_attribute` = '.(int)$id_attr;
}
$sql = 'SELECT c.`id_product_attribute`
FROM `'._DB_PREFIX_.'product_attribute` c
JOIN `'._DB_PREFIX_.'product_attribute_combination` pac ON c.`id_product_attribute` = pac.`id_product_attribute`
JOIN `'._DB_PREFIX_.'product_attribute_combination` pac
ON c.`id_product_attribute` = pac.`id_product_attribute`
WHERE c.`id_product` = '.(int)$id_product.'
GROUP BY c.`id_product_attribute`
HAVING SUM(pac.`id_attribute` = '.implode(' OR pac.`id_attribute` = ', array_map('intval', $attributeIds)).') = '.count($attributeIds);
HAVING SUM('.implode(' OR ', $conditions).') = '.count($attributeIds);
$result = Db::getInstance()->getRow($sql);
return $result ? new Combination($result['id_product_attribute']) : false;
return $result ? new Combination((int)$result['id_product_attribute']) : false;
}
// Parse XML and group products by Symbol
// === GRUPOWANIE PRODUKTÓW PO SYMBOLU ===
$productsBySymbol = [];
foreach ($xml->product as $productData) {
$symbol = (string)$productData->item_group_id;
@@ -140,7 +182,117 @@ foreach ($xml->product as $productData) {
$productsBySymbol[$symbol][] = $productData;
}
// Create or update products with combinations based on grouped data
// =======================================
// =========== TRYB AKTUALIZACJI =========
// =======================================
if ($modeUpdate) {
$today = date('Y-m-d');
$updatedToday = [];
// Wczytanie logu aktualizacji
if (file_exists($logFile)) {
$lines = file($logFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
foreach ($lines as $line) {
$parts = explode(';', $line);
if (count($parts) >= 3) {
$logDate = trim($parts[0]);
$logType = trim($parts[1]); // np. 'product'
$logRef = trim($parts[2]);
if ($logDate === $today) {
$updatedToday[$logType.'_'.$logRef] = true;
}
}
}
}
$updatedSomething = false;
foreach ($productsBySymbol as $symbol => $products) {
if (empty($products)) {
continue;
}
// Główny produkt referencja z pierwszego elementu grupy
$mainProductData = $products[0];
$reference = (string)$mainProductData->sku;
$key = 'product_'.$reference;
// Jeśli już zaktualizowany dzisiaj pomijamy
if (isset($updatedToday[$key])) {
continue;
}
$product = findProductByReference($reference);
if (!$product) {
// produkt nie istnieje w Presta - pomijamy w trybie update
continue;
}
// Nowa cena z XML
$newPrice = floatval(str_replace(',', '', (string)$mainProductData->price));
if ($newPrice <= 0) {
// brak sensownej ceny pomiń
continue;
}
// Aktualizacja ceny produktu
$product->price = $newPrice;
// Sprawdź kategorię domyślną jeśli "Strona główna", zamień na "Meble" (ID 107)
$id_lang = (int)$context->language->id;
$defaultCategory = new Category($product->id_category_default, $id_lang);
if (Validate::isLoadedObject($defaultCategory) && $defaultCategory->name == 'Strona główna') {
$newCategoryId = 107; // Meble
// Ustaw nową kategorię domyślną
$product->id_category_default = (int)$newCategoryId;
// Podmień kategorie produktu (zachowując ewentualne inne)
$categories = $product->getCategories();
// Usuń starą kategorię domyślną, jeśli istnieje w tablicy
$categories = array_diff($categories, [(int)$defaultCategory->id]);
$categories[] = (int)$newCategoryId;
$categories = array_unique(array_map('intval', $categories));
$product->updateCategories($categories);
}
// Zapis produktu
if ($product->update()) {
// Zapis do logu że ten produkt został dziś zaktualizowany
$logLine = $today.';product;'.$reference.';'.$product->id.PHP_EOL;
file_put_contents($logFile, $logLine, FILE_APPEND);
echo '<p>Zaktualizowano produkt: '.htmlspecialchars((string)$mainProductData->title).' ('.$reference.')</p>';
echo '<p>Nowa cena: '.$newPrice.'</p>';
$updatedSomething = true;
}
// Aktualizujemy tylko jeden produkt na jedno wywołanie
break;
}
if ($updatedSomething) {
// Odśwież stronę, żeby przy kolejnym wywołaniu zaktualizować następny produkt
echo '<script>setTimeout(function(){ location.reload(); }, 250);</script>';
} else {
echo '<p>Brak produktów do aktualizacji na dzisiaj (wszystkie z XML zostały już zaktualizowane).</p>';
}
exit;
}
// =======================================
// =========== TRYB DODAWANIA ============
// =======================================
$productAdded = false;
$combinationAdded = false;
// Tworzenie lub aktualizacja produktów z kombinacjami (dodawanie)
foreach ($productsBySymbol as $symbol => $products) {
if (empty($products)) {
continue;
@@ -150,23 +302,24 @@ foreach ($productsBySymbol as $symbol => $products) {
$mainProductData = $products[0];
$mainProduct = findProductByReference((string)$mainProductData->sku);
$productAdded = false;
if (!$mainProduct) {
// Create a new product if it doesn't exist
$mainProduct = new Product();
$mainProduct->name = createMultiLangField((string)$mainProductData->title);
$description = (string)$mainProductData->description;
$description = str_replace("\n", "<br>", $description);
$mainProduct->description = createMultiLangField($description);
$mainProduct->price = floatval(str_replace(',','',$mainProductData->price));
$mainProduct->price = floatval(str_replace(',', '', (string)$mainProductData->price));
$mainProduct->reference = (string)$mainProductData->sku;
$mainProduct->id_category_default = 2;
$mainProduct->id_category_default = 2; // np. Strona główna
$mainProduct->link_rewrite = createLinkRewrite((string)$mainProductData->title);
$mainProduct->add();
// Add images to the product
if (!empty($mainProductData->image)) {
addProductImage($mainProduct->id, $mainProductData->image);
addProductImage($mainProduct->id, (string)$mainProductData->image);
}
for ($i = 1; $i <= 10; $i++) {
@@ -177,15 +330,6 @@ foreach ($productsBySymbol as $symbol => $products) {
}
$productAdded = true;
} else {
// Update existing product price and description
if ( $config['update_price'] == true ) {
$mainProduct->price = floatval(str_replace(',','',$mainProductData->price));
$description = (string)$mainProductData->description;
$description = str_replace("\n", "<br>", $description);
$mainProduct->description = createMultiLangField($description);
$mainProduct->update();
}
}
// Ensure the product is saved before adding combinations
@@ -198,14 +342,13 @@ foreach ($productsBySymbol as $symbol => $products) {
$addedCombinations = [];
// Add or update combinations for each product in the group
$combinationAdded = false;
foreach ($products as $productData) {
$attributes = [
'Kolor' => (string)$productData->Kolor,
'Dlugosc' => (string)$productData->Dlugosc,
'Kolor' => (string)$productData->Kolor,
'Dlugosc' => (string)$productData->Dlugosc,
'Szerokosc' => (string)$productData->Szerokosc,
'Glebokosc' => (string)$productData->Glebokosc,
'Wysokosc' => (string)$productData->Wysokosc,
'Wysokosc' => (string)$productData->Wysokosc,
];
$attributeIds = [];
@@ -214,7 +357,7 @@ foreach ($productsBySymbol as $symbol => $products) {
$attributeGroupId = createAttribute($name, [$value]);
$attribute = findAttributeByName($attributeGroupId, $value);
if ($attribute) {
$attributeIds[] = $attribute->id;
$attributeIds[] = (int)$attribute->id;
}
}
}
@@ -224,12 +367,12 @@ foreach ($productsBySymbol as $symbol => $products) {
$key = implode('-', $attributeIds);
// Add or update combination if it is unique
if (!empty($attributeIds)) {
if (!empty($attributeIds) && !isset($addedCombinations[$key])) {
$combination = findCombinationByAttributes($mainProduct->id, $attributeIds);
if (!$combination) {
// Create new combination
$combination = new Combination();
$combination->id_product = $mainProduct->id;
$combination->id_product = (int)$mainProduct->id;
$combination->quantity = 100; // Default quantity, you can adjust this
$combination->reference = (string)$productData->sku;
$combination->add();
@@ -256,18 +399,19 @@ foreach ($productsBySymbol as $symbol => $products) {
Product::updateDefaultAttribute($mainProduct->id);
if ($productAdded || $combinationAdded) {
if ($productAdded) {
echo "<p>Dodałem produkt: " . (string)$mainProductData->title . "</p>";
}
if ($combinationAdded) {
echo "<p>Dodałem kombinację: " . (string)$mainProductData->title . "</p>";
}
break; // Break if a new product or combination was added
if ($productAdded) {
echo "<p>Dodałem produkt: " . htmlspecialchars((string)$mainProductData->title) . "</p>";
}
if ($combinationAdded) {
echo "<p>Dodałem kombinację: " . htmlspecialchars((string)$mainProductData->title) . "</p>";
}
break; // Break if a new product or combination was added
}
}
// reload page after 1s if product or combination was added
// reload page after 250ms if product or combination was added (dla trybu add)
if ($productAdded || $combinationAdded) {
echo "<script>setTimeout(function(){location.reload();}, 250);</script>";
}
?>