From 8ce350a646cc620a09d64013b98fa035280f1382 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Fri, 18 Apr 2025 00:01:59 +0200 Subject: [PATCH] =?UTF-8?q?Dodaj=20plik=20import-product.php=20oraz=20funk?= =?UTF-8?q?cje=20do=20importu=20produkt=C3=B3w=20z=20XML?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .vscode/ftp-kr.sync.cache.json | 4 +- import-product.php | 588 +++++++++++++++++++++++++++++++++ 2 files changed, 590 insertions(+), 2 deletions(-) create mode 100644 import-product.php diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index c53d9655..35a13fe5 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -708,8 +708,8 @@ }, "Product.php": { "type": "-", - "size": 309303, - "lmtime": 0, + "size": 317879, + "lmtime": 1744577629429, "modified": false }, "ProductPresenterFactory.php": { diff --git a/import-product.php b/import-product.php new file mode 100644 index 00000000..0ecba1ec --- /dev/null +++ b/import-product.php @@ -0,0 +1,588 @@ +]*>)/', '$1$2_$3', $xmlString); // Zamień dwukropki na podkreślniki +$xml = simplexml_load_string($xmlString, 'SimpleXMLElement', LIBXML_NOCDATA); +if (!$xml) { + foreach (libxml_get_errors() as $error) { + echo "XML error: {$error->message}\n"; + } + die("Error: Cannot parse XML"); +} + +// Logowanie czasu wykonania +function logTime($message, $startTime) { + file_put_contents('import_log.txt', $message . ' - Czas: ' . round(microtime(true) - $startTime, 10) . " s\n", FILE_APPEND); +} + +$startGlobal = microtime(true); + +// 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); + return $result ? new AttributeGroup($result['id_attribute_group']) : false; +} + +// Function to find attribute by name +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; + $result = Db::getInstance()->getRow($sql); + return $result ? new Attribute($result['id_attribute']) : false; +} + +// Function to check if a product already has images +function productHasImages($productId) { + $images = Image::getImages(Context::getContext()->language->id, $productId); + return !empty($images); +} + +// Function to create attribute if it doesn't exist +function createAttribute($name, $values) { + $attributeGroup = findAttributeGroupByName($name); + if (!$attributeGroup) { + $attributeGroup = new AttributeGroup(); + $attributeGroup->name = createMultiLangField($name); + $attributeGroup->public_name = createMultiLangField($name); + $attributeGroup->group_type = 'select'; + $attributeGroup->add(); + + Db::getInstance()->insert('attribute_group_shop', [ + 'id_attribute_group' => (int)$attributeGroup->id, + 'id_shop' => 1 + ], false, true, Db::REPLACE); + } + + foreach ($values as $value) { + $attribute = findAttributeByName($attributeGroup->id, $value); + if (!$attribute) { + $attribute = new Attribute(); + $attribute->id_attribute_group = $attributeGroup->id; + $attribute->name = createMultiLangField($value); + $attribute->add(); + + Db::getInstance()->insert('attribute_shop', [ + 'id_attribute' => (int)$attribute->id, + 'id_shop' => 1 + ], false, true, Db::REPLACE); + } + } + + return $attributeGroup->id; +} + +// Helper function to create a multi-language field +function createMultiLangField($field) { + $languages = Language::getLanguages(false); + $res = []; + foreach ($languages as $lang) { + $res[$lang['id_lang']] = $field; + } + return $res; +} + +// Helper function to create a valid link_rewrite +function createLinkRewrite($field) { + $languages = Language::getLanguages(false); + $res = []; + $linkRewrite = Tools::link_rewrite($field); // PrestaShop's function to create valid link_rewrite + foreach ($languages as $lang) { + $res[$lang['id_lang']] = $linkRewrite; + } + return $res; +} + +// Function to get category ID from name +function getCategoryId($categoryName) { + $category = Category::searchByName(1, $categoryName); // 1 for default language id + if (!empty($category)) { + return $category['id_category']; + } else { + // Create category if not exists + $category = new Category(); + $category->name = createMultiLangField($categoryName); + $category->link_rewrite = createLinkRewrite($categoryName); + $category->id_parent = 2; // Default parent category + $category->add(); + return $category->id; + } +} + +// Function to log added images +function logAddedImage($productId, $imageUrl) { + $logFile = dirname(__FILE__) . '/image_log.json'; + $logData = file_exists($logFile) ? json_decode(file_get_contents($logFile), true) : []; + if (!isset($logData[$productId])) { + $logData[$productId] = []; + } + $logData[$productId][] = $imageUrl; + file_put_contents($logFile, json_encode($logData)); +} + +// Function to check if an image has been added +function isImageAdded($productId, $imageUrl) { + $logFile = dirname(__FILE__) . '/image_log.json'; + if (!file_exists($logFile)) { + return false; + } + $logData = json_decode(file_get_contents($logFile), true); + return isset($logData[$productId]) && in_array($imageUrl, $logData[$productId]); +} + +// Function to download image from URL and associate it with a product +function addProductImage($productId, $imageUrl) { + // if (isImageAdded($productId, $imageUrl)) { + // return false; // Skip duplicate images + // } + + $image = new Image(); + $image->id_product = $productId; + $image->position = Image::getHighestPosition($productId) + 1; + $image->add(); + + $imagePath = $image->getPathForCreation(); + $url = str_replace(' ', '%20', $imageUrl); + if (!copy($url, $imagePath . '.jpg')) { + $image->delete(); + return false; + } + + $imageTypes = ImageType::getImagesTypes('products'); + foreach ($imageTypes as $imageType) { + ImageManager::resize($imagePath . '.jpg', $imagePath . '-' . stripslashes($imageType['name']) . '.jpg', $imageType['width'], $imageType['height']); + } + + logAddedImage($productId, $imageUrl); + return $image->id; +} + +// Function to set the first image as cover +function setCoverImage($productId) { + $images = Image::getImages(Context::getContext()->language->id, $productId); + if (!empty($images)) { + $firstImage = reset($images); + $firstImageId = $firstImage['id_image']; + Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'image` SET cover = 0 WHERE id_product = '.(int)$productId); + Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'image` SET cover = 1 WHERE id_image = '.(int)$firstImageId); + Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'image_shop` SET cover = 0 WHERE id_product = '.(int)$productId); + Db::getInstance()->execute('UPDATE `'._DB_PREFIX_.'image_shop` SET cover = 1 WHERE id_image = '.(int)$firstImageId); + } +} + +// 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; +} + +// Function to find combination by product ID and attribute IDs +function findCombinationByAttributes($id_product, $attributeIds) { + $attributeIds = array_unique($attributeIds); + sort($attributeIds); + $searchKey = implode('-', $attributeIds); + + $sql = ' + SELECT pa.id_product_attribute + FROM ps_product_attribute pa + JOIN ps_product_attribute_combination pac ON pac.id_product_attribute = pa.id_product_attribute + WHERE pa.id_product = '.(int)$id_product; + + $combinations = Db::getInstance()->executeS($sql); + + foreach ($combinations as $combo) { + $comboAttrIds = Db::getInstance()->executeS(' + SELECT id_attribute FROM ps_product_attribute_combination + WHERE id_product_attribute = '.(int)$combo['id_product_attribute'].' + '); + $comboAttrIds = array_column($comboAttrIds, 'id_attribute'); + sort($comboAttrIds); + if (implode('-', $comboAttrIds) === $searchKey) { + return new Combination($combo['id_product_attribute']); + } + } + + return false; +} + +// Function to assign manufacturer to product +function assignManufacturerToProduct($productId, $manufacturerId) { + $product = new Product($productId); + $product->id_manufacturer = $manufacturerId; + $product->update(); +} + +// Function to assign feature to product (without searching for IDs) +function assignFeatureToProduct($productId, $featureId, $featureValueId) { + $existingFeature = Db::getInstance()->getRow( + 'SELECT id_feature_product FROM `'._DB_PREFIX_.'feature_product` WHERE `id_product` = '.(int)$productId.' AND `id_feature` = '.(int)$featureId + ); + + if (!$existingFeature) { + Db::getInstance()->insert('feature_product', [ + 'id_product' => (int)$productId, + 'id_feature' => (int)$featureId, + 'id_feature_value' => (int)$featureValueId + ]); + } else { + Db::getInstance()->update('feature_product', [ + 'id_feature_value' => (int)$featureValueId + ], 'id_feature_product = '.(int)$existingFeature['id_feature_product']); + } +} + +// Function to update additional delivery parameters +function updateDeliveryParameters($productId) { + $product = new Product($productId); + $product->additional_delivery_times = 2; + $product->delivery_in_stock = 'ok. 2-5 dni roboczych'; + $product->delivery_out_stock = 'ok. 2-5 dni roboczych'; + $product->update(); +} + +// Function to calculate combination price impact +function calculatePriceImpact($basePrice, $combinationPrice) { + return floatval(str_replace(',', '', $combinationPrice)) - floatval(str_replace(',', '', $basePrice)); +} + +function addProductToShops($product) { + $shops = [1]; + foreach ($shops as $shopId) { + Db::getInstance()->execute('INSERT IGNORE INTO `'._DB_PREFIX_.'product_shop` (`id_product`, `id_shop`, `id_category_default`, `price`, `active`) + VALUES ('.(int)$product->id.', '.(int)$shopId.', 2, '.(float)$product->price.', 1)'); + } +} + +// Funkcja do przypisania kombinacji do obu sklepów +function addCombinationToShops($combination) { + $shops = [1]; + foreach ($shops as $shopId) { + Db::getInstance()->execute('INSERT IGNORE INTO `'._DB_PREFIX_.'product_attribute_shop` (`id_product_attribute`, `id_product`, `id_shop`, `price`) + VALUES ('.(int)$combination->id.', '.(int)$combination->id_product.', '.(int)$shopId.', '.(float)$combination->price.')'); + } +} + +// Funkcja do dodawania obrazów do obu sklepów +function addImageToShops($imageId, $productId) { + $shops = [1]; + foreach ($shops as $shopId) { + Db::getInstance()->execute('INSERT IGNORE INTO `'._DB_PREFIX_.'image_shop` (`id_image`, `id_product`, `id_shop`, `cover`) + VALUES ('.(int)$imageId.', '.(int)$productId.', '.(int)$shopId.', 0)'); + } +} + +// Function to parse and import product features +function importProductFeatures($productId, $featuresString) { + $features = explode('|', $featuresString); + foreach ($features as $feature) { + list($featureName, $featureValue) = explode('>', $feature, 2); + + if ( trim( $featureValue ) == 'Rozmiar' or $featureValue == 'ROZMIAR' or strpos( $featureName, 'filtr rozwijany' ) !== false ) + continue; + + $featureName = trim($featureName); + $featureValue = trim($featureValue); + + $featureId = Db::getInstance()->getValue( + 'SELECT id_feature FROM `'._DB_PREFIX_.'feature_lang` WHERE name = "' . pSQL($featureName) . '"' + ); + + if (!$featureId) { + $feature = new Feature(); + $feature->name = createMultiLangField($featureName); + $feature->add(); + $featureId = $feature->id; + } + + if ( $featureId == 21 or $featureId == 36 ) + continue; + + $featureValueId = Db::getInstance()->getValue( + 'SELECT fv.id_feature_value FROM `'._DB_PREFIX_.'feature_value` fv + JOIN `'._DB_PREFIX_.'feature_value_lang` fvl ON fv.id_feature_value = fvl.id_feature_value + WHERE fvl.value = "' . pSQL($featureValue) . '" AND fv.id_feature = ' . (int)$featureId + ); + + if (!$featureValueId) { + $featureValueObj = new FeatureValue(); + $featureValueObj->id_feature = $featureId; + $featureValueObj->value = createMultiLangField($featureValue); + $featureValueObj->add(); + $featureValueId = $featureValueObj->id; + } + + Db::getInstance()->insert('feature_product', [ + 'id_product' => (int)$productId, + 'id_feature' => (int)$featureId, + 'id_feature_value' => (int)$featureValueId + ]); + } +} + +function sortAttributesByGroup($attributeIds) { + $sorted = []; + foreach ($attributeIds as $id) { + $groupId = Db::getInstance()->getValue('SELECT id_attribute_group FROM '._DB_PREFIX_.'attribute WHERE id_attribute = '.(int)$id); + $sorted[$groupId] = $id; + } + ksort($sorted); // Sortuj po ID grupy rosnąco + return array_values($sorted); +} + +echo '
';
+// Parse XML and group products by Symbol
+$productsBySymbol = [];
+foreach ( $xml -> post as $productData )
+{
+  $i = 1;
+  $product_tmp = (array)$productData;
+
+  $Dostępnerozmiaryjeślidotyczy = $product_tmp['Dostępnerozmiaryjeślidotyczy:untitled_3'];
+  $sku = $product_tmp['KodEAN-13'];
+  $title = $product_tmp['Nazwaproduktu'] . ' AMZ';
+  $description = $product_tmp['Długiopis'];
+  $short_description = $product_tmp['Krótkiopis'];
+  $price = $product_tmp['CenaBRUTTO'];
+  $rozmiar = $product_tmp['Dostępnerozmiaryjeślidotyczy:untitled_3'];
+  $kolor = $product_tmp['Dostępnekoloryjeślidotyczy:untitled_2'];
+
+  $images = explode( '|', $product_tmp['ZdjęciaproduktuURL-e'] );
+
+  if ( !count( $images ) or ( count( $images ) == 1 and empty( $images[0] ) ) )
+  {
+    if ( isset( $product_images[ $symbol ]['images'] ) )
+      $images = $product_images[ $symbol ]['images'];
+  }
+
+  foreach ( $images as $image )
+  {
+    $productData->{'images_' . $i} = $image;
+    $i++;
+  }
+
+  $symbol = md5( $productData -> Nazwaproduktu );
+
+  $productData->sku = $sku;
+  $productData->title = $title;
+  $productData->description = $description;
+  $productData->short_description = $short_description;
+  $productData->price = $price;
+  $productData->rozmiar = $rozmiar;
+  $productData->kolor = $kolor;
+
+  if ( !isset( $productsBySymbol[$symbol] ) ) {
+    $productsBySymbol[$symbol] = [];
+  }
+
+  if ( strpos( $Dostępnerozmiaryjeślidotyczy, '|' ) === false )
+    $productsBySymbol[$symbol][] = $productData;
+  else
+  {
+    $product_images[ $symbol ]['images'] = $images;
+  }
+}
+foreach ($productsBySymbol as $symbol => $products)
+{
+    if ( empty( $products ) ) {
+      continue;
+    }
+
+
+    $mainProductData = $products[0];
+    $mainProduct = findProductByReference((string)$mainProductData->sku);
+
+    $amz_category = (string)$mainProductData->Kategorieproduktu;
+    if ( strpos( $amz_category, 'Kołdry' ) !== false ) {
+      $id_category_default = 124;
+    } elseif ( strpos( $amx_category, 'Poduszka' ) !== false or strpos( $amz_category, 'poduszka' ) !== false or strpos( $amz_category, 'Poduszki' ) !== false ) {
+      $id_category_default = 117;
+    } else {
+      $id_category_default = 2;
+    }
+
+    // print_r($mainProductData);
+    $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", "
", $description); + $mainProduct->description = createMultiLangField($description); + $mainProduct->price = floatval(str_replace(',','',$mainProductData->price)); + $mainProduct->reference = (string)$mainProductData->sku; + // ean13 + $mainProduct->ean13 = (string)$mainProductData->sku; + $mainProduct->id_category_default = $id_category_default; + $mainProduct->link_rewrite = createLinkRewrite((string)$mainProductData->title); + $mainProduct->id_shop_list = [1]; // Przypisanie do sklepów + $mainProduct->add(); + + // ❗ Teraz przypisanie do kategorii + $mainProduct->updateCategories([$id_category_default, $id_category_default]); + + foreach ($categories as $catId) { + foreach ([1] as $shopId) { // tu wpisz ID sklepu, np. 2 + Db::getInstance()->insert('category_product', [ + 'id_category' => (int)$catId, + 'id_product' => (int)$mainProduct->id, + 'position' => 1 // lub kolejność jaką chcesz + ], false, true, Db::REPLACE); + } + } + + Db::getInstance()->insert('product_shop', [ + 'id_product' => (int)$mainProduct->id, + 'id_shop' => 1, + 'id_category_default' => (int)$id_category_default, + 'price' => (float)$mainProduct->price, + 'active' => 1, + ], false, true, Db::REPLACE); + + $mainProduct->update(); + + addProductToShops($mainProduct); + + if (!empty($mainProductData->Cechyproduktu)) { + importProductFeatures($mainProduct->id, (string)$mainProductData->Cechyproduktu); + } + + // Ensure the product has combinations enabled + $mainProduct->checkDefaultAttributes(); + + Product::updateDefaultAttribute($mainProduct->id); + assignFeatureToProduct($mainProduct->id, 11, 157); + assignManufacturerToProduct($mainProduct->id, 32); + updateDeliveryParameters($mainProduct->id); + + $productAdded = true; + } + else + { + if ( $config['update_price'] == true ) + { + $mainProduct->price = floatval(str_replace(',','',$mainProductData->price)); + $description = (string)$mainProductData->description; + $description = str_replace("\n", "
", $description); + $mainProduct->description = createMultiLangField($description); + $mainProduct->update(); + } + } + + // Ensure the product is saved before adding combinations + if (!$mainProduct->id) { + echo "Failed to create or update main product: " . (string)$mainProductData->title . "\n"; + continue; + } + + // Ensure the combination set is unique for the product + $addedCombinations = []; + + // Add or update combinations for each product in the group + $combinationAdded = false; + foreach ($products as $productData) + { + $attributes = [ + 'Kolor' => (string)$productData->kolor, + 'Rozmiar' => (string)$productData->rozmiar + ]; + + $attributeIds = []; + foreach ($attributes as $name => $value) { + if (!empty($value)) { + $attributeGroupId = createAttribute($name, [$value]); + $attribute = findAttributeByName($attributeGroupId, $value); + if ($attribute) { + $attributeIds[] = $attribute->id; + } + } + } + + // Create a unique key for the attribute set + $attributeIds = sortAttributesByGroup($attributeIds); + $key = implode('-', $attributeIds); + + // Add or update combination if it is unique + if (!empty($attributeIds)) + { + $combination = findCombinationByAttributes($mainProduct->id, $attributeIds); + + if (!$combination) { + // Create new combination + $combination = new Combination(); + $combination->id_product = $mainProduct->id; + $combination->quantity = 100; // Default quantity, you can adjust this + $combination->reference = (string)$productData->sku; + // ean13 + $combination->ean13 = (string)$productData->sku; + $combination->id_shop_list = [1]; // Przypisanie do sklepów + $combination->price = calculatePriceImpact($mainProductData->price, $productData->price); + $combination->add(); + + $attributeIds = array_unique($attributeIds); + $attributeIds = sortAttributesByGroup($attributeIds); // funkcja z poprzedniej wiadomości + + $combination->setAttributes($attributeIds); + $combination->save(); + if (isset($addedCombinations[$key])) { + continue; // już dodana + + if (!$combination) {} + addCombinationToShops($combination); + } + + $combinationAdded = true; + } else { + // Update existing combination quantity if necessary + $combination->quantity = 100; // Update quantity, you can adjust this + $combination->update(); + } + + // Mark this combination as added + $addedCombinations[$key] = true; + } + + if ( $combinationAdded ) + { + if ( !productHasImages($mainProduct->id) and !empty($productData->images_2) ) + { + for ($i = 1; $i <= 10; $i++) { + $imageUrl = (string)$productData->{'images_' . $i}; + if (!empty($imageUrl)) { + $imageId = addProductImage($mainProduct->id, $imageUrl); + if ($imageId) { + addImageToShops($imageId, $mainProduct->id); + } + } + } + } + setCoverImage($mainProduct->id); + + break; // Break if a new combination was added + } + } + + if ($productAdded || $combinationAdded) { + if ($productAdded) { + echo "

Dodałem produkt: " . (string)$mainProductData->title . "

"; + } + if ($combinationAdded) { + echo "

Dodałem kombinację: " . (string)$mainProductData->title . "

"; + } + break; // Break if a new product or combination was added + } +} +// reload page after 1s if product or combination was added +if ($productAdded || $combinationAdded) { + echo ""; +} +?> \ No newline at end of file