diff --git a/ecomzone/classes/EcomZoneProductSync.php b/ecomzone/classes/EcomZoneProductSync.php index 4ce10f6..d43451b 100644 --- a/ecomzone/classes/EcomZoneProductSync.php +++ b/ecomzone/classes/EcomZoneProductSync.php @@ -2,269 +2,265 @@ class EcomZoneProductSync { - private $client; - - public function __construct() - { - $this->client = new EcomZoneClient(); + private $client; + + public function __construct() + { + $this->client = new EcomZoneClient(); + } + + private function resizeImage($src, $dest, $width, $height) + { + list($srcWidth, $srcHeight, $type) = getimagesize($src); + $srcImage = $this->createImageFromType($src, $type); + + $destImage = imagecreatetruecolor($width, $height); + imagecopyresampled($destImage, $srcImage, 0, 0, 0, 0, $width, $height, $srcWidth, $srcHeight); + + $this->saveImageFromType($destImage, $dest, $type); + + imagedestroy($srcImage); + imagedestroy($destImage); + } + + private function createImageFromType($filename, $type) + { + switch ($type) { + case IMAGETYPE_JPEG: + return imagecreatefromjpeg($filename); + case IMAGETYPE_PNG: + return imagecreatefrompng($filename); + case IMAGETYPE_GIF: + return imagecreatefromgif($filename); + default: + throw new Exception("Unsupported image type: " . $type); } - - private function resizeImage($src, $dest, $width, $height) - { - list($srcWidth, $srcHeight, $type) = getimagesize($src); - $srcImage = $this->createImageFromType($src, $type); - - $destImage = imagecreatetruecolor($width, $height); - imagecopyresampled($destImage, $srcImage, 0, 0, 0, 0, $width, $height, $srcWidth, $srcHeight); - - $this->saveImageFromType($destImage, $dest, $type); - - imagedestroy($srcImage); - imagedestroy($destImage); + } + + private function saveImageFromType($image, $filename, $type) + { + switch ($type) { + case IMAGETYPE_JPEG: + $this->createDirectoryIfNotExists(dirname($filename)); + imagejpeg($image, $filename); + break; + case IMAGETYPE_PNG: + $this->createDirectoryIfNotExists(dirname($filename)); + imagepng($image, $filename); + break; + case IMAGETYPE_GIF: + $this->createDirectoryIfNotExists(dirname($filename)); + imagegif($image, $filename); + break; + case IMAGETYPE_GIF: + imagegif($image, $filename); + break; + default: + throw new Exception("Unsupported image type: " . $type); } - - private function createImageFromType($filename, $type) - { - switch ($type) { - case IMAGETYPE_JPEG: - return imagecreatefromjpeg($filename); - case IMAGETYPE_PNG: - return imagecreatefrompng($filename); - case IMAGETYPE_GIF: - return imagecreatefromgif($filename); - default: - throw new Exception("Unsupported image type: " . $type); - } - } - - private function saveImageFromType($image, $filename, $type) - { - switch ($type) { - case IMAGETYPE_JPEG: - $this->createDirectoryIfNotExists(dirname($filename)); - imagejpeg($image, $filename); - break; - case IMAGETYPE_PNG: - $this->createDirectoryIfNotExists(dirname($filename)); - imagepng($image, $filename); - break; - case IMAGETYPE_GIF: - $this->createDirectoryIfNotExists(dirname($filename)); - imagegif($image, $filename); - break; - case IMAGETYPE_GIF: - imagegif($image, $filename); - break; - default: - throw new Exception("Unsupported image type: " . $type); - } - } - - - public function importProducts($perPage = 100) - { - $page = 1; - $totalImported = 0; - $totalAvailable = 0; + } - EcomZoneLogger::log("Starting product import - perPage: $perPage"); - EcomZoneLogger::log("Number of products to be imported per page: $perPage", 'INFO'); + public function importProducts($perPage = 100) + { + $page = 1; + $totalImported = 0; + $totalAvailable = 0; - try { - do { - $catalog = $this->client->getCatalog($page, $perPage); -$importedCount = 0; -foreach ($catalog['data'] as $product) { - if ($this->importSingleProduct($product)) { - $importedCount++; - } -} + EcomZoneLogger::log("Starting product import - perPage: $perPage"); -$totalImported += $importedCount; -$totalAvailable = $catalog['total']; -$page++; + EcomZoneLogger::log("Number of products to be imported per page: $perPage", 'INFO'); -EcomZoneLogger::log("Imported page $page", 'INFO', [ - 'total_imported' => $totalImported, - 'page' => $page, - 'total_available' => $totalAvailable -]); - - } while ($page <= ceil($totalAvailable / $perPage) && isset($catalog['next_page_url']) && $catalog['next_page_url'] !== null); - - EcomZoneLogger::log("Finished importing products", 'INFO', [ - 'total_imported' => $totalImported, - 'total_available' => $totalAvailable - ]); - - // Clear cache - Tools::clearSmartyCache(); - Tools::clearXMLCache(); - Media::clearCache(); - PrestaShopAutoload::getInstance()->generateIndex(); - - return [ - 'success' => true, - 'imported' => $totalImported, - 'total' => $totalAvailable - ]; - - } catch (Exception $e) { - EcomZoneLogger::log("Error importing products: " . $e->getMessage(), 'ERROR'); - throw $e; - } - } - - private function importSingleProduct($productData) - { - // Extract data from nested structure - $data = isset($productData['data']) ? $productData['data'] : $productData; - - if (!isset($data['sku']) || !isset($data['product_name']) || !isset($data['description']) || !isset($data['product_price'])) { - EcomZoneLogger::log("Invalid product data", 'ERROR', ['data' => $productData]); - return false; + try { + do { + $catalog = $this->client->getCatalog($page, $perPage); + $importedCount = 0; + foreach ($catalog['data'] as $product) { + if ($this->importSingleProduct($product)) { + $importedCount++; + } } - try { - // Check if product already exists by reference - $productId = Db::getInstance()->getValue(' + $totalImported += $importedCount; + $totalAvailable = $catalog['total']; + $page++; + + EcomZoneLogger::log("Imported page $page", 'INFO', [ + 'total_imported' => $totalImported, + 'page' => $page, + 'total_available' => $totalAvailable + ]); + } while ($page <= ceil($totalAvailable / $perPage) && isset($catalog['next_page_url']) && $catalog['next_page_url'] !== null); + + EcomZoneLogger::log("Finished importing products", 'INFO', [ + 'total_imported' => $totalImported, + 'total_available' => $totalAvailable + ]); + + // Clear cache + Tools::clearSmartyCache(); + Tools::clearXMLCache(); + Media::clearCache(); + PrestaShopAutoload::getInstance()->generateIndex(); + + return [ + 'success' => true, + 'imported' => $totalImported, + 'total' => $totalAvailable + ]; + } catch (Exception $e) { + EcomZoneLogger::log("Error importing products: " . $e->getMessage(), 'ERROR'); + throw $e; + } + } + + private function importSingleProduct($productData) + { + // Extract data from nested structure + $data = isset($productData['data']) ? $productData['data'] : $productData; + + if (!isset($data['sku']) || !isset($data['product_name']) || !isset($data['description']) || !isset($data['product_price'])) { + EcomZoneLogger::log("Invalid product data", 'ERROR', ['data' => $productData]); + return false; + } + + try { + // Check if product already exists by reference + $productId = Db::getInstance()->getValue(' SELECT id_product FROM ' . _DB_PREFIX_ . 'product WHERE reference = "' . pSQL($data['sku']) . '" '); - $product = $productId ? new Product($productId) : new Product(); - - $defaultLangId = (int)Configuration::get('PS_LANG_DEFAULT'); - - $product->reference = $data['sku']; - $product->name[$defaultLangId] = $data['product_name']; - $product->description[$defaultLangId] = $data['long_description'] ?? $data['description']; - $product->description_short[$defaultLangId] = $data['description']; - $product->price = $data['product_price']; - $product->active = true; - $product->quantity = (int)$data['stock']; - $homeCategoryId = (int)Configuration::get('PS_HOME_CATEGORY'); - $product->id_category_default = $homeCategoryId; - $product->addToCategories([$homeCategoryId]); - $product->visibility = 'both'; // Ensure product is visible in both catalog and search - $product->active = true; - $product->quantity = (int)$data['stock']; - $homeCategoryId = (int)Configuration::get('PS_HOME_CATEGORY'); - $product->id_category_default = $homeCategoryId; - $product->addToCategories([$homeCategoryId]); - - // Save product first to get ID - if (!$product->id) { - $product->add(); - } else { - $product->update(); - } -StockAvailable::setQuantity($product->id, 0, (int)$data['stock']); -$product->available_for_order = true; // Ensure product is available for order -$product->show_price = true; // Ensure price is shown + $product = $productId ? new Product($productId) : new Product(); - // Handle image import if URL is provided - if (isset($data['image']) && !empty($data['image'])) { - $this->importProductImage($product, $data['image']); - } - - StockAvailable::setQuantity($product->id, 0, (int)$data['stock']); - - EcomZoneLogger::log("Imported product", 'INFO', [ - 'sku' => $data['sku'], - 'id' => $product->id, - 'name' => $data['product_name'] - ]); - - return true; - - } catch (Exception $e) { - EcomZoneLogger::log("Error importing product", 'ERROR', [ - 'sku' => $data['sku'], - 'error' => $e->getMessage() - ]); - return false; - } - } + $defaultLangId = (int)Configuration::get('PS_LANG_DEFAULT'); + $product->reference = $data['sku']; + $product->name[$defaultLangId] = $data['product_name']; + $product->description[$defaultLangId] = $data['long_description'] ?? $data['description']; + $product->description_short[$defaultLangId] = $data['description']; + $product->price = $data['product_price']; + $product->active = true; + $product->quantity = (int)$data['stock']; + $homeCategoryId = (int)Configuration::get('PS_HOME_CATEGORY'); + $product->id_category_default = $homeCategoryId; + $product->addToCategories([$homeCategoryId]); + $product->visibility = 'both'; // Ensure product is visible in both catalog and search + $product->active = true; + $product->quantity = (int)$data['stock']; + $homeCategoryId = (int)Configuration::get('PS_HOME_CATEGORY'); + $product->id_category_default = $homeCategoryId; + $product->addToCategories([$homeCategoryId]); + // Save product first to get ID + if (!$product->id) { + $product->add(); + } else { + $product->update(); + } + StockAvailable::setQuantity($product->id, 0, (int)$data['stock']); + $product->available_for_order = true; // Ensure product is available for order + $product->show_price = true; // Ensure price is shown -private function importProductImage($product, $imageUrl) -{ - try { - // Create temporary file - $tmpFile = tempnam(_PS_TMP_IMG_DIR_, 'ecomzone_'); - - // Download image - if (!copy($imageUrl, $tmpFile)) { - throw new Exception("Failed to download image from: " . $imageUrl); - } - - // Get image info - $imageInfo = getimagesize($tmpFile); - if (!$imageInfo) { - unlink($tmpFile); - throw new Exception("Invalid image file"); - } - - // Validate image dimensions and file size - if ($imageInfo[0] > 2000 || $imageInfo[1] > 2000 || filesize($tmpFile) > 5000000) { - unlink($tmpFile); - throw new Exception("Image dimensions or file size exceed limits"); - } - - // Generate unique name - $imageName = $product->reference . '-' . time() . '.' . pathinfo($imageUrl, PATHINFO_EXTENSION); - - // Delete existing images if any - $product->deleteImages(); - - // Add new image - $image = new Image(); - $image->id_product = $product->id; - $image->position = 1; - $image->cover = true; - - // Save the image to the correct directory - $imagePath = _PS_PROD_IMG_DIR_ . $image->getImgPath() . '.' . $image->image_format; - $this->createDirectoryIfNotExists(dirname($imagePath)); - if (!copy($tmpFile, $imagePath)) { - unlink($tmpFile); - throw new Exception("Failed to save image to: " . $imagePath); - } + // Handle image import if URL is provided + if (isset($data['image']) && !empty($data['image'])) { + $this->importProductImage($product, $data['image']); + } - // Associate the image with the product - if (!$image->add()) { - unlink($tmpFile); - throw new Exception("Failed to add image to product"); - } + StockAvailable::setQuantity($product->id, 0, (int)$data['stock']); - // Manually resize the image and generate thumbnails - $this->resizeImage($imagePath, _PS_PROD_IMG_DIR_ . $image->getImgPath() . '-home_default.' . $image->image_format, 250, 250); - $this->resizeImage($imagePath, _PS_PROD_IMG_DIR_ . $image->getImgPath() . '-large_default.' . $image->image_format, 800, 800); + EcomZoneLogger::log("Imported product", 'INFO', [ + 'sku' => $data['sku'], + 'id' => $product->id, + 'name' => $data['product_name'] + ]); - // Cleanup - unlink($tmpFile); - EcomZoneLogger::log("Imported product image", 'INFO', [ - 'sku' => $product->reference, - 'image' => $imageUrl - ]); - + return true; } catch (Exception $e) { - EcomZoneLogger::log("Error importing product image", 'ERROR', [ - 'sku' => $product->reference, - 'image' => $imageUrl, - 'error' => $e->getMessage() - ]); + EcomZoneLogger::log("Error importing product", 'ERROR', [ + 'sku' => $data['sku'], + 'error' => $e->getMessage() + ]); + return false; } -} + } -private function createDirectoryIfNotExists($directory) -{ - if (!is_dir($directory)) { - mkdir($directory, 0755, true); + + + private function importProductImage($product, $imageUrl) + { + try { + // Create temporary file + $tmpFile = tempnam(_PS_TMP_IMG_DIR_, 'ecomzone_'); + + // Download image + if (!copy($imageUrl, $tmpFile)) { + throw new Exception("Failed to download image from: " . $imageUrl); + } + + // Get image info + $imageInfo = getimagesize($tmpFile); + if (!$imageInfo) { + unlink($tmpFile); + throw new Exception("Invalid image file"); + } + + // Validate image dimensions and file size + if ($imageInfo[0] > 2000 || $imageInfo[1] > 2000 || filesize($tmpFile) > 5000000) { + unlink($tmpFile); + throw new Exception("Image dimensions or file size exceed limits"); + } + + // Generate unique name + $imageName = $product->reference . '-' . time() . '.' . pathinfo($imageUrl, PATHINFO_EXTENSION); + + // Delete existing images if any + $product->deleteImages(); + + // Add new image + $image = new Image(); + $image->id_product = $product->id; + $image->position = 1; + $image->cover = true; + + // Save the image to the correct directory + $imagePath = _PS_PROD_IMG_DIR_ . $image->getImgPath() . '.' . $image->image_format; + $this->createDirectoryIfNotExists(dirname($imagePath)); + if (!copy($tmpFile, $imagePath)) { + unlink($tmpFile); + throw new Exception("Failed to save image to: " . $imagePath); + } + + // Associate the image with the product + if (!$image->add()) { + unlink($tmpFile); + throw new Exception("Failed to add image to product"); + } + + // Manually resize the image and generate thumbnails + $this->resizeImage($imagePath, _PS_PROD_IMG_DIR_ . $image->getImgPath() . '-home_default.' . $image->image_format, 250, 250); + $this->resizeImage($imagePath, _PS_PROD_IMG_DIR_ . $image->getImgPath() . '-large_default.' . $image->image_format, 800, 800); + + // Cleanup + unlink($tmpFile); + EcomZoneLogger::log("Imported product image", 'INFO', [ + 'sku' => $product->reference, + 'image' => $imageUrl + ]); + } catch (Exception $e) { + EcomZoneLogger::log("Error importing product image", 'ERROR', [ + 'sku' => $product->reference, + 'image' => $imageUrl, + 'error' => $e->getMessage() + ]); } -} + } + + private function createDirectoryIfNotExists($directory) + { + if (!is_dir($directory)) { + mkdir($directory, 0755, true); + } + } }