* @copyright 2015 PrestaShow.pl * @license http://PrestaShow.pl/license */ class PShow_Combination_Generator { private static $instance = null; public static function getInstance() { if (self::$instance === null) { self::$instance = new PShow_Combination_Generator(); } return self::$instance; } /** * Create product combinations * * @param array $list * @return array */ protected function createCombinations($list) { if (count($list) <= 1) { return count($list) ? array_map(create_function('$v', 'return (array($v));'), reset($list)) : $list; } $res = array(); $first = array_pop($list); foreach ($first as $attribute) { $tab = $this->createCombinations($list); foreach ($tab as $to_add) { $res[] = is_array($to_add) ? array_merge($to_add, array($attribute)) : array($to_add, $attribute); } } return $res; } /** * Set product combinations impact * * @param integer $id_product * @param array $tab * @return boolean */ protected function setAttributesImpacts($id_product, $tab) { $attributes = array(); foreach ($tab as $group) { foreach ($group as $attribute) { $price = preg_replace('/[^0-9.]/', '', str_replace(',', '.', 0)); //Tools::getValue('price_impact_' . (int) $attribute))); $weight = preg_replace('/[^0-9.]/', '', str_replace(',', '.', 0)); //Tools::getValue('weight_impact_' . (int) $attribute))); $attributes[] = '(' . (int) $id_product . ', ' . (int) $attribute . ', ' . (float) $price . ', ' . (float) $weight . ')'; } } return Db::getInstance()->execute(' INSERT INTO `' . _DB_PREFIX_ . 'attribute_impact` (`id_product`, `id_attribute`, `price`, `weight`) VALUES ' . implode(',', $attributes) . ' ON DUPLICATE KEY UPDATE `price` = VALUES(price), `weight` = VALUES(weight)'); } public function generateMultipleCombinations($combinations, $attributes, $product) { $res = true; $default_on = 1; foreach ($combinations as $key => $combination) { $id_combination = (int) $product->productAttributeExists($attributes[$key], false, null, true, true); $obj = new Combination($id_combination, null, PShow_Import::getInstance()->id_shop); if ($id_combination) { $obj->minimal_quantity = 1; $obj->available_date = '0000-00-00'; } foreach ($combination as $field => $value) { if ($field == 'images') { continue; } $obj->$field = $value; } $obj->default_on = $default_on; $default_on = 0; $product->setAvailableDate(); try { $obj->save(); } catch (PrestaShopDatabaseException $e) { $obj->default_on = false; $obj->save(); } $obj->setImages($combination['images']); if (!$id_combination) { $attribute_list = array(); foreach ($attributes[$key] as $id_attribute) { $attribute_list[] = array( 'id_product_attribute' => (int) $obj->id, 'id_attribute' => (int) $id_attribute ); } $res &= Db::getInstance()->insert('product_attribute_combination', $attribute_list); } } return $res; } public function setFirstCombinationAsDefault($id_product) { $id_product = (int) $id_product; $q = 'UPDATE IGNORE `ps_product_attribute_shop` SET `default_on` = NULL WHERE `id_product` = ' . $id_product . '; UPDATE IGNORE `ps_product_attribute` SET `default_on` = NULL WHERE `id_product` = ' . $id_product . '; UPDATE IGNORE `ps_product_attribute_shop` SET `default_on` = 1 WHERE `id_product` = ' . $id_product . ' AND `id_product_attribute` IN ( 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`) ORDER BY pa.`id_product_attribute` ASC ); UPDATE IGNORE `ps_product_attribute` SET `default_on` = 1 WHERE `id_product` = ' . $id_product . ' AND `id_product_attribute` IN ( SELECT `id_product_attribute` FROM `ps_product_attribute_shop` WHERE `default_on` = 1 );'; Db::getInstance()->query($q); } /** * Generate all possible combinations for product * * @param integer $id_product */ public function generateCombinationsForProduct($id_product) { if (PShow_Import::getInstance() && Module::getInstanceByName("pagecache")) { PShow_Log::addImportLog( "Disabling module: pagecache..." . (Module::disableByName("pagecache") ? 'success' : 'failed') ); } if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Generating all possible combinations for product id: " . $id_product ); } if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Getting all attributes from product..." ); } // get all product attributes $q = "SELECT DISTINCT pac.`id_attribute`, a.`id_attribute_group` " . "FROM `" . _DB_PREFIX_ . "product_attribute_combination` pac " . "JOIN `" . _DB_PREFIX_ . "product_attribute` pa ON (pa.`id_product_attribute` = pac.`id_product_attribute`) " . "JOIN `" . _DB_PREFIX_ . "attribute` a ON (a.`id_attribute` = pac.`id_attribute`) " . "WHERE pa.`id_product` = " . (int) $id_product; $attributes = Db::getInstance()->executeS($q); // product don't have any combinations if (!count($attributes)) { if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Product don't have any attributes assigned... " ); } return; } if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Preparing attributes array..." ); } $tab = array(); foreach ($attributes as $k => $attribute) { if (!isset($tab[$attribute['id_attribute_group']])) { $tab[$attribute['id_attribute_group']] = array(); } array_push($tab[$attribute['id_attribute_group']], $attribute['id_attribute']); } // product have only one group of attributes - module won't generate any new if (count($tab) <= 1) { if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Generating have only one group of attributes, skipping..." ); } $this->setFirstCombinationAsDefault($id_product); return; } $product = new Product((int) $id_product, false, null, PShow_Import::getInstance()->id_shop); if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Getting attributes impact..." ); } $this->setAttributesImpacts($id_product, $tab); if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Preparing combinations..." ); } $combinations = array_values($this->createCombinations($tab)); $imagesToRemove = array(); $values = array(); foreach ($combinations as $attributes) { if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Preparing combination values for attributes: " . implode(',', $attributes) ); } $images = array(); $q = "SELECT pai.`id_image`, pai.`id_product_attribute` FROM `" . _DB_PREFIX_ . "product_attribute_image` pai JOIN `" . _DB_PREFIX_ . "product_attribute` pa ON (pa.`id_product` = " . $id_product . " AND pa.`id_product_attribute` = pai.`id_product_attribute`) WHERE pai.`id_product_attribute` IN ( SELECT pac.`id_product_attribute` FROM `" . _DB_PREFIX_ . "product_attribute_combination` pac WHERE (pac.`id_attribute` = " . implode(' OR pac.`id_attribute` = ', $attributes) . ") AND pac.`id_product_attribute` > 0 )"; $rows = Db::getInstance(_PS_USE_SQL_SLAVE_)->executeS($q); if (count($rows)) { foreach ($rows as $row) { $imagesToRemove[] = $row; if (in_array((int) $row['id_image'], $images)) continue; array_push($images, (int) $row['id_image']); } unset($rows); } $reference = uniqid($product->id . '-' . $product->reference . '-', false); $values[] = array( 'id_product' => (int) $product->id, 'price' => (float) 0, 'weight' => (float) 0, 'ecotax' => 0, 'images' => $images, 'quantity' => (int) 0, 'reference' => $reference, 'default_on' => 0, 'available_date' => '0000-00-00' ); } if (PShow_Import::getInstance() && Module::getInstanceByName("pagecache")) { PShow_Log::addImportLog( "Enabling module: pagecache..." . (Module::enableByName("pagecache") ? 'success' : 'failed') ); } if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Removing current product combinations..." ); } // remove all existing product combinations $product->deleteProductAttributes(); $q = "DELETE FROM `" . _DB_PREFIX_ . "product_attribute_image` " . "WHERE `id_product_attribute` = 0; "; $q .= "DELETE FROM `" . _DB_PREFIX_ . "product_attribute_image` " . "WHERE `id_image` NOT IN (SELECT `id_image` FROM `" . _DB_PREFIX_ . "image`); "; $q .= "DELETE FROM `" . _DB_PREFIX_ . "product_attribute_image` " . "WHERE `id_product_attribute` NOT IN (" . "SELECT `id_product_attribute` FROM `" . _DB_PREFIX_ . "product_attribute` WHERE `id_product` > 0" . "); "; foreach ($imagesToRemove as $img) { $q .= "DELETE IGNORE FROM `" . _DB_PREFIX_ . "product_attribute_image` " . "WHERE `id_product_attribute` = " . $img['id_product_attribute'] . " " . "AND `id_image` = " . $img['id_image'] . "; "; } Db::getInstance()->query($q); if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Generating new combinations..." ); } // generate combinations $this->generateMultipleCombinations($values, $combinations, $product); if (PShow_Import::getInstance()) { PShow_Log::addImportLog( "Setting first combination as default..." ); } $this->setFirstCombinationAsDefault($id_product); } }