workingCopy/ecomzone/classes/EcomZoneProductImport.php

346 lines
10 KiB
PHP

<?php
if (!defined("_PS_VERSION_")) {
exit();
}
class EcomZoneProductImport
{
private $module;
private $defaultLangId;
private $defaultShopId;
public function __construct()
{
$this->module = Module::getInstanceByName("ecomzone");
$this->defaultLangId = (int) Configuration::get("PS_LANG_DEFAULT");
$this->defaultShopId = (int) Context::getContext()->shop->id;
}
public function importProduct($productData)
{
try {
$data = $productData["data"];
// Start transaction
Db::getInstance()->execute("START TRANSACTION");
// Get or create product
$product = $this->getOrCreateProduct($data["sku"]);
// Update basic information
$this->updateBasicInfo($product, $data);
// Update categories
$this->updateCategories($product, $data);
// Update images
$this->updateImages($product, $data);
// Update specifications
$this->updateSpecifications($product, $data);
// Update stock
$this->updateStock($product, $data);
// Commit transaction
Db::getInstance()->execute("COMMIT");
EcomZoneLogger::log("Product imported successfully", "INFO", [
"sku" => $data["sku"],
]);
return true;
} catch (Exception $e) {
Db::getInstance()->execute("ROLLBACK");
EcomZoneLogger::log("Product import failed", "ERROR", [
"sku" => $data["sku"] ?? "unknown",
"error" => $e->getMessage(),
]);
throw $e;
}
}
private function getOrCreateProduct($sku)
{
$productId = Product::getIdByReference($sku);
if ($productId) {
$product = new Product($productId);
} else {
$product = new Product();
$product->reference = $sku;
}
return $product;
}
private function updateBasicInfo($product, $data)
{
$product->name[$this->defaultLangId] = $data["product_name"];
$product->description[$this->defaultLangId] = $data["long_description"];
$product->description_short[$this->defaultLangId] =
$data["description"];
$product->price = (float) $data["product_price"];
$product->wholesale_price =
(float) ($data["recommended_retail_price"] ?? 0);
$product->ean13 = $data["ean"] ?? "";
// Product settings
$product->active = true;
$product->visibility = "both";
$product->available_for_order = true;
$product->show_price = true;
$product->indexed = 1;
// Dimensions
if (!empty($data["dimensions"])) {
$dimensions = json_decode($data["dimensions"], true);
$product->weight = (float) ($dimensions["weight"] ?? 0);
$product->height = (float) ($dimensions["height"] ?? 0);
$product->width = (float) ($dimensions["width"] ?? 0);
$product->depth = (float) ($dimensions["length"] ?? 0);
}
if (!$product->id) {
$product->add();
} else {
$product->update();
}
}
private function updateCategories($product, $data)
{
$categoryIds = [];
// Process main categories
if (!empty($data["categories"])) {
$categories = json_decode($data["categories"], true);
foreach ($categories as $categoryName) {
if (strpos($categoryName, "0") === 0) {
continue;
} // Skip special categories
$categoryId = $this->getOrCreateCategory($categoryName);
if ($categoryId) {
$categoryIds[] = $categoryId;
}
}
}
// Process Google categories
if (!empty($data["google_categories"])) {
$googleCategories = json_decode($data["google_categories"], true);
foreach ($googleCategories as $categoryName) {
$categoryId = $this->getOrCreateCategory($categoryName);
if ($categoryId) {
$categoryIds[] = $categoryId;
}
}
}
// Ensure we have at least default category
if (empty($categoryIds)) {
$categoryIds[] = (int) Configuration::get("PS_HOME_CATEGORY");
}
$categoryIds = array_unique($categoryIds);
$product->id_category_default = reset($categoryIds);
$product->updateCategories($categoryIds);
}
private function getOrCreateCategory($name)
{
$name = trim($name);
if (empty($name)) {
return null;
}
// Try to find existing category
$categoryId = (int) Db::getInstance()->getValue(
'SELECT c.id_category
FROM `' .
_DB_PREFIX_ .
'category_lang` cl
JOIN `' .
_DB_PREFIX_ .
'category` c ON c.id_category = cl.id_category
WHERE cl.name = "' .
pSQL($name) .
'"
AND cl.id_lang = ' .
(int) $this->defaultLangId
);
if ($categoryId) {
return $categoryId;
}
// Create new category
$category = new Category();
$category->name = [$this->defaultLangId => $name];
$category->link_rewrite = [
$this->defaultLangId => Tools::str2url($name),
];
$category->id_parent = (int) Configuration::get("PS_HOME_CATEGORY");
$category->active = true;
if ($category->add()) {
return (int) $category->id;
}
return null;
}
private function updateImages($product, $data)
{
$imageUrls = [];
// Main image
if (!empty($data["image"])) {
$imageUrls[] = $data["image"];
}
// Additional images
if (!empty($data["images"])) {
$additionalImages = json_decode($data["images"], true);
if (is_array($additionalImages)) {
$imageUrls = array_merge($imageUrls, $additionalImages);
}
}
// GIF or video thumbnail
if (!empty($data["gif_or_video"])) {
$imageUrls[] = $data["gif_or_video"];
}
if (empty($imageUrls)) {
return;
}
// Delete existing images
$product->deleteImages();
// Import new images
foreach ($imageUrls as $index => $imageUrl) {
$this->importImage($product, $imageUrl, $index === 0);
}
}
private function importImage($product, $imageUrl, $cover = false)
{
$tmpFile = tempnam(_PS_TMP_IMG_DIR_, "ecomzone_");
try {
if (!copy($imageUrl, $tmpFile)) {
throw new Exception("Failed to download image");
}
$image = new Image();
$image->id_product = $product->id;
$image->position = Image::getHighestPosition($product->id) + 1;
$image->cover = $cover;
if (!$image->add()) {
throw new Exception("Failed to create image record");
}
$imagesTypes = ImageType::getImagesTypes("products");
foreach ($imagesTypes as $type) {
ImageManager::resize(
$tmpFile,
_PS_PROD_IMG_DIR_ .
$image->getExistingImgPath() .
"." .
$type["name"] .
".jpg",
$type["width"],
$type["height"]
);
}
// Save original image
ImageManager::resize(
$tmpFile,
_PS_PROD_IMG_DIR_ . $image->getExistingImgPath() . ".jpg"
);
} catch (Exception $e) {
EcomZoneLogger::log("Image import failed", "ERROR", [
"url" => $imageUrl,
"error" => $e->getMessage(),
]);
} finally {
if (file_exists($tmpFile)) {
unlink($tmpFile);
}
}
}
private function updateSpecifications($product, $data)
{
if (empty($data["specifications"])) {
return;
}
try {
$specifications = json_decode($data["specifications"], true);
if (!is_array($specifications)) {
return;
}
foreach ($specifications as $name => $value) {
if (empty($value)) {
continue;
}
// Get or create feature
$featureId = Feature::getIdByName($name);
if (!$featureId) {
$feature = new Feature();
$feature->name[$this->defaultLangId] = $name;
if (!$feature->add()) {
continue;
}
$featureId = $feature->id;
}
// Get or create feature value
$featureValue = new FeatureValue();
$featureValue->id_feature = $featureId;
$featureValue->value[$this->defaultLangId] = $value;
if (!$featureValue->add()) {
continue;
}
// Associate with product
$product->addFeaturesToDB([$featureId => $featureValue->id]);
}
} catch (Exception $e) {
EcomZoneLogger::log("Failed to update specifications", "ERROR", [
"sku" => $product->reference,
"error" => $e->getMessage(),
]);
}
}
private function updateStock($product, $data)
{
try {
$quantity = (int) ($data["stock_quantity"] ?? $data["stock"] ?? 0);
StockAvailable::setQuantity($product->id, 0, $quantity);
// Update out of stock behavior
StockAvailable::setProductOutOfStock(
$product->id,
$quantity > 0 ? 2 : 0, // 2 = Allow orders, 0 = Deny orders
$this->defaultShopId
);
} catch (Exception $e) {
throw new EcomZoneException(
"Failed to update stock: " . $e->getMessage(),
EcomZoneException::INVALID_PRODUCT_DATA,
$e
);
}
}
}