jmauclair Posted July 8, 2021 Share Posted July 8, 2021 (edited) Hi, I'm developing a custom module for my shop. This module will generate product packs with a unique product reference. I want to take the original product images to set it to the pack freshly created with this product. What I need is : Find an efficient way to set multiple products to the same images Something really light because it will be used for a really big amount of product packs (like 3k) What I've got is : All original product and packs informations url of home_default type images Waiting for you help guys 😉!! Edited July 15, 2021 by jmauclair SOLVED (by myself lol) (see edit history) Link to comment Share on other sites More sharing options...
jmauclair Posted July 15, 2021 Author Share Posted July 15, 2021 (edited) Finally find a way to do this easily The packedproductID is the ID of the new product and the productID is the ID of the original product, the one you want to take pictures. function setProductImage($packedProductID, $productID) { $packedProdImgs = getProductImage($packedProductID); foreach ($packedProdImgs as $imgs) { $image = new Image(); $image->id_product = $productID; $image->position = $imgs['position']; if ($imgs['cover'] == 1) { $image->cover = true; // or false; } else { $image->cover = false; } if ($image->add()) { if (!copyImg($productID, $image->id, $imgs['url'], 'products', true)) { $image->delete(); } } } } Aux functions : function copyImg($id_entity, $id_image = null, $url, $entity = 'products', $regenerate = false) { $tmpfile = tempnam(_PS_TMP_IMG_DIR_, 'ps_import'); $watermark_types = explode(',', Configuration::get('WATERMARK_TYPES')); switch ($entity) { default: case 'products': $image_obj = new Image($id_image); $path = $image_obj->getPathForCreation(); break; } $url = urldecode(trim($url)); $parced_url = parse_url($url); if (isset($parced_url['path'])) { $uri = ltrim($parced_url['path'], '/'); $parts = explode('/', $uri); foreach ($parts as &$part) { $part = rawurlencode($part); } unset($part); $parced_url['path'] = '/' . implode('/', $parts); } if (isset($parced_url['query'])) { $query_parts = array(); parse_str($parced_url['query'], $query_parts); $parced_url['query'] = http_build_query($query_parts); } if (!function_exists('http_build_url')) { require_once(_PS_TOOL_DIR_ . 'http_build_url/http_build_url.php'); } $url = http_build_url('', $parced_url); $orig_tmpfile = $tmpfile; if (Tools::copy($url, $tmpfile)) { // Evaluate the memory required to resize the image: if it's too much, you can't resize it. if (!ImageManager::checkImageMemoryLimit($tmpfile)) { @unlink($tmpfile); return false; } $tgt_width = $tgt_height = 0; $src_width = $src_height = 0; $error = 0; ImageManager::resize($tmpfile, $path . '.jpg', null, null, 'jpg', false, $error, $tgt_width, $tgt_height, 5, $src_width, $src_height); $images_types = ImageType::getImagesTypes($entity, true); if ($regenerate) { $previous_path = null; $path_infos = array(); $path_infos[] = array($tgt_width, $tgt_height, $path . '.jpg'); foreach ($images_types as $image_type) { $tmpfile = get_best_path($image_type['width'], $image_type['height'], $path_infos); if (ImageManager::resize($tmpfile, $path . '-' . stripslashes($image_type['name']) . '.jpg', $image_type['width'], $image_type['height'], 'jpg', false, $error, $tgt_width, $tgt_height, 5, $src_width, $src_height)) { // the last image should not be added in the candidate list if it's bigger than the original image if ($tgt_width <= $src_width && $tgt_height <= $src_height) { $path_infos[] = array($tgt_width, $tgt_height, $path . '-' . stripslashes($image_type['name']) . '.jpg'); } if ($entity == 'products') { if (is_file(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '.jpg')) { unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '.jpg'); } if (is_file(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '_' . (int) Context::getContext()->shop->id . '.jpg')) { unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '_' . (int) Context::getContext()->shop->id . '.jpg'); } } } if (in_array($image_type['id_image_type'], $watermark_types)) { Hook::exec('actionWatermark', array('id_image' => $id_image, 'id_product' => $id_entity)); } } } } else { @unlink($orig_tmpfile); return false; } unlink($orig_tmpfile); return true; } function get_best_path($tgt_width, $tgt_height, $path_infos) { $path_infos = array_reverse($path_infos); $path = ''; foreach ($path_infos as $path_info) { list($width, $height, $path) = $path_info; if ($width >= $tgt_width && $height >= $tgt_height) { return $path; } } return $path; } These functions are normally available in AdminImportController Edited July 15, 2021 by jmauclair (see edit history) Link to comment Share on other sites More sharing options...
GUNEZ Posted July 25, 2022 Share Posted July 25, 2022 On 7/15/2021 at 5:19 PM, jmauclair said: Finally find a way to do this easily The packedproductID is the ID of the new product and the productID is the ID of the original product, the one you want to take pictures. function setProductImage($packedProductID, $productID) { $packedProdImgs = getProductImage($packedProductID); foreach ($packedProdImgs as $imgs) { $image = new Image(); $image->id_product = $productID; $image->position = $imgs['position']; if ($imgs['cover'] == 1) { $image->cover = true; // or false; } else { $image->cover = false; } if ($image->add()) { if (!copyImg($productID, $image->id, $imgs['url'], 'products', true)) { $image->delete(); } } } } Aux functions : function copyImg($id_entity, $id_image = null, $url, $entity = 'products', $regenerate = false) { $tmpfile = tempnam(_PS_TMP_IMG_DIR_, 'ps_import'); $watermark_types = explode(',', Configuration::get('WATERMARK_TYPES')); switch ($entity) { default: case 'products': $image_obj = new Image($id_image); $path = $image_obj->getPathForCreation(); break; } $url = urldecode(trim($url)); $parced_url = parse_url($url); if (isset($parced_url['path'])) { $uri = ltrim($parced_url['path'], '/'); $parts = explode('/', $uri); foreach ($parts as &$part) { $part = rawurlencode($part); } unset($part); $parced_url['path'] = '/' . implode('/', $parts); } if (isset($parced_url['query'])) { $query_parts = array(); parse_str($parced_url['query'], $query_parts); $parced_url['query'] = http_build_query($query_parts); } if (!function_exists('http_build_url')) { require_once(_PS_TOOL_DIR_ . 'http_build_url/http_build_url.php'); } $url = http_build_url('', $parced_url); $orig_tmpfile = $tmpfile; if (Tools::copy($url, $tmpfile)) { // Evaluate the memory required to resize the image: if it's too much, you can't resize it. if (!ImageManager::checkImageMemoryLimit($tmpfile)) { @unlink($tmpfile); return false; } $tgt_width = $tgt_height = 0; $src_width = $src_height = 0; $error = 0; ImageManager::resize($tmpfile, $path . '.jpg', null, null, 'jpg', false, $error, $tgt_width, $tgt_height, 5, $src_width, $src_height); $images_types = ImageType::getImagesTypes($entity, true); if ($regenerate) { $previous_path = null; $path_infos = array(); $path_infos[] = array($tgt_width, $tgt_height, $path . '.jpg'); foreach ($images_types as $image_type) { $tmpfile = get_best_path($image_type['width'], $image_type['height'], $path_infos); if (ImageManager::resize($tmpfile, $path . '-' . stripslashes($image_type['name']) . '.jpg', $image_type['width'], $image_type['height'], 'jpg', false, $error, $tgt_width, $tgt_height, 5, $src_width, $src_height)) { // the last image should not be added in the candidate list if it's bigger than the original image if ($tgt_width <= $src_width && $tgt_height <= $src_height) { $path_infos[] = array($tgt_width, $tgt_height, $path . '-' . stripslashes($image_type['name']) . '.jpg'); } if ($entity == 'products') { if (is_file(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '.jpg')) { unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '.jpg'); } if (is_file(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '_' . (int) Context::getContext()->shop->id . '.jpg')) { unlink(_PS_TMP_IMG_DIR_ . 'product_mini_' . (int) $id_entity . '_' . (int) Context::getContext()->shop->id . '.jpg'); } } } if (in_array($image_type['id_image_type'], $watermark_types)) { Hook::exec('actionWatermark', array('id_image' => $id_image, 'id_product' => $id_entity)); } } } } else { @unlink($orig_tmpfile); return false; } unlink($orig_tmpfile); return true; } function get_best_path($tgt_width, $tgt_height, $path_infos) { $path_infos = array_reverse($path_infos); $path = ''; foreach ($path_infos as $path_info) { list($width, $height, $path) = $path_info; if ($width >= $tgt_width && $height >= $tgt_height) { return $path; } } return $path; } These functions are normally available in AdminImportController Hi @jmauclair i want to use same images several products. Is this code is work for that? Can u help me please? Link to comment Share on other sites More sharing options...
jmauclair Posted July 25, 2022 Author Share Posted July 25, 2022 53 minutes ago, GUNEZ said: Hi @jmauclair i want to use same images several products. Is this code is work for that? Can u help me please? Hi @GUNEZ, yes this code might still work as it was working one year ago, have you tried it ? By the way, before sending a direct message to me, please let me the time to answer on the post, that’s not polite Link to comment Share on other sites More sharing options...
GUNEZ Posted July 25, 2022 Share Posted July 25, 2022 1 minute ago, jmauclair said: Hi @GUNEZ, yes this code might still work as it was working one year ago, have you tried it ? By the way, before sending a direct message to me, please let me the time to answer on the post, that’s not polite I'm so sorry for direct message. I'm searching 2 weeks. I'm not try. I will add the code below AdminImportController.php . Is this correct? function setProductImage($packedProductID, $productID) { $packedProdImgs = getProductImage($packedProductID); foreach ($packedProdImgs as $imgs) { $image = new Image(); $image->id_product = $productID; $image->position = $imgs['position']; if ($imgs['cover'] == 1) { $image->cover = true; // or false; } else { $image->cover = false; } if ($image->add()) { if (!copyImg($productID, $image->id, $imgs['url'], 'products', true)) { $image->delete(); } } } } Link to comment Share on other sites More sharing options...
jmauclair Posted July 25, 2022 Author Share Posted July 25, 2022 Before asking for help you should try. No, you should use this code in a php file, you don’t have to put it in AdminImportController.php. I recommend you to make some research about how is working PHP and how to make simple scripts for Prestashop. 1 Link to comment Share on other sites More sharing options...
GUNEZ Posted July 25, 2022 Share Posted July 25, 2022 (edited) Thank you for your advices. I will try addding code in custom.js Edited July 25, 2022 by GUNEZ (see edit history) Link to comment Share on other sites More sharing options...
GUNEZ Posted July 25, 2022 Share Posted July 25, 2022 I guess I couldn't. I can't use same image other product. Link to comment Share on other sites More sharing options...
jmauclair Posted August 24, 2022 Author Share Posted August 24, 2022 On 7/25/2022 at 9:44 AM, GUNEZ said: Thank you for your advices. I will try addding code in custom.js It's php, so if you put it in a JS file it won't work. You should take a look at that : https://www.codingdojo.com/blog/best-free-web-development-courses-beginners Link to comment Share on other sites More sharing options...
knacky Posted August 25, 2022 Share Posted August 25, 2022 (edited) Hi. When I read it like that, I can't help but wonder. Making copies of images for such a large number of products is nonsense. Let's count. You have 3,000 products and each product has 5 thumbnails for one image. There are 3 products in the package. So by copying images you create 3000 x 5 x 3 = 450,000 images, which is approx. 17GB. If the product has multiple images, the result needs to be multiplied by the number of images. The solution is to create your own table in the database and write the product id, the image id of the original product into it. And then create an override Product.php and function getImages(). The solution is always simple 😉 I'll write the override code when I get to the computer. Edited August 25, 2022 by knacky (see edit history) Link to comment Share on other sites More sharing options...
knacky Posted August 25, 2022 Share Posted August 25, 2022 (edited) ./override/classes/Product.php This code does not copy any images. It finds out the ID of the products in the package and creates a simple SQL query to the database and returns the ID of the images of the individual products !!! You can also edit the code so that it only shows images of the variant in the package. Just load the id_product_attribute item and find out the image of the attribute / combination. class Product extends ProductCore { public function getImages($id_lang, Context $context = null) { $productsInPack = array(); if (Pack::isPack((int) $this->id)) { $getProductInPack = Db::getInstance()->executeS('SELECT id_product_item FROM '._DB_PREFIX_.'pack WHERE id_product_pack = '.$this->id.' GROUP BY id_product_item'); if ($getProductInPack){ foreach ($getProductInPack as $productPack){ $productsInPack[] = $productPack['id_product_item']; } } } if ($productsInPack){ $getProducts = implode(', ', $productsInPack); } else { $getProducts = $this->id; } $query = ' SELECT image_shop.`cover`, i.`id_image`, il.`legend`, i.`position` FROM `' . _DB_PREFIX_ . 'image` i ' . Shop::addSqlAssociation('image', 'i') . ' LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ') WHERE i.`id_product` IN (' . $getProducts . ') ORDER BY `position`'; return Db::getInstance()->executeS($query); } public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, \Context $context = null) { parent::__construct($id_product, $full, $id_lang, $id_shop, $context); } } BO: FO: Edited August 25, 2022 by knacky (see edit history) Link to comment Share on other sites More sharing options...
knacky Posted August 25, 2022 Share Posted August 25, 2022 (edited) Sample for loading attribute/combination images. When there is no combination, it loads the default product image from the package. class Product extends ProductCore { public function getImages($id_lang, Context $context = null) { $productsInPack = array(); $productsAttributeItem = array(); if (Pack::isPack((int) $this->id)) { $getProductInPack = Db::getInstance()->executeS('SELECT id_product_item, id_product_attribute_item FROM '._DB_PREFIX_.'pack WHERE id_product_pack = '.$this->id.' GROUP BY id_product_item'); if ($getProductInPack > 0){ foreach ($getProductInPack as $productPack){ if ($productPack['id_product_attribute_item']){ $getAttributeImage = Db::getInstance()->executeS('SELECT id_image FROM '._DB_PREFIX_.'product_attribute_image WHERE id_product_attribute = '.$productPack['id_product_attribute_item'].' GROUP BY id_image'); foreach ($getAttributeImage as $attributeImages){ $productsAttributeItem[] = $attributeImages['id_image']; } } if ($productPack['id_product_attribute_item'] == '0'){ $getImage = Db::getInstance()->executeS('SELECT id_image FROM '._DB_PREFIX_.'image WHERE id_product = '.$productPack['id_product_item']); foreach ($getImage as $image){ $productsAttributeItem[] = $image['id_image']; } } $productsInPack[] = $productPack['id_product_item']; } } } if ($productsInPack){ $getProducts = implode(', ', $productsInPack); } else { $getProducts = $this->id; } if ($productsAttributeItem){ $whereAttributeImages = ' AND i.id_image IN ('.implode(', ', $productsAttributeItem).')'; } else { $whereAttributeImages = ''; } $query = ' SELECT image_shop.`cover`, i.`id_image`, il.`legend`, i.`position` FROM `' . _DB_PREFIX_ . 'image` i ' . Shop::addSqlAssociation('image', 'i') . ' LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ') WHERE i.`id_product` IN (' . $getProducts . ')'.$whereAttributeImages.' ORDER BY `position`'; return Db::getInstance()->executeS($query); } public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, \Context $context = null) { parent::__construct($id_product, $full, $id_lang, $id_shop, $context); } } FO: Edited August 25, 2022 by knacky (see edit history) Link to comment Share on other sites More sharing options...
jmauclair Posted October 18, 2022 Author Share Posted October 18, 2022 On 8/25/2022 at 7:16 AM, knacky said: Sample for loading attribute/combination images. When there is no combination, it loads the default product image from the package. class Product extends ProductCore { public function getImages($id_lang, Context $context = null) { $productsInPack = array(); $productsAttributeItem = array(); if (Pack::isPack((int) $this->id)) { $getProductInPack = Db::getInstance()->executeS('SELECT id_product_item, id_product_attribute_item FROM '._DB_PREFIX_.'pack WHERE id_product_pack = '.$this->id.' GROUP BY id_product_item'); if ($getProductInPack > 0){ foreach ($getProductInPack as $productPack){ if ($productPack['id_product_attribute_item']){ $getAttributeImage = Db::getInstance()->executeS('SELECT id_image FROM '._DB_PREFIX_.'product_attribute_image WHERE id_product_attribute = '.$productPack['id_product_attribute_item'].' GROUP BY id_image'); foreach ($getAttributeImage as $attributeImages){ $productsAttributeItem[] = $attributeImages['id_image']; } } if ($productPack['id_product_attribute_item'] == '0'){ $getImage = Db::getInstance()->executeS('SELECT id_image FROM '._DB_PREFIX_.'image WHERE id_product = '.$productPack['id_product_item']); foreach ($getImage as $image){ $productsAttributeItem[] = $image['id_image']; } } $productsInPack[] = $productPack['id_product_item']; } } } if ($productsInPack){ $getProducts = implode(', ', $productsInPack); } else { $getProducts = $this->id; } if ($productsAttributeItem){ $whereAttributeImages = ' AND i.id_image IN ('.implode(', ', $productsAttributeItem).')'; } else { $whereAttributeImages = ''; } $query = ' SELECT image_shop.`cover`, i.`id_image`, il.`legend`, i.`position` FROM `' . _DB_PREFIX_ . 'image` i ' . Shop::addSqlAssociation('image', 'i') . ' LEFT JOIN `' . _DB_PREFIX_ . 'image_lang` il ON (i.`id_image` = il.`id_image` AND il.`id_lang` = ' . (int) $id_lang . ') WHERE i.`id_product` IN (' . $getProducts . ')'.$whereAttributeImages.' ORDER BY `position`'; return Db::getInstance()->executeS($query); } public function __construct($id_product = null, $full = false, $id_lang = null, $id_shop = null, \Context $context = null) { parent::__construct($id_product, $full, $id_lang, $id_shop, $context); } } FO: That's way bettter but it seems that Prestashop 1.7 will not use override anymore Source : Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now