From c6de0d892356d2ad63b0052430ded77360775b03 Mon Sep 17 00:00:00 2001 From: fatmaBouchekoua Date: Mon, 8 May 2017 17:13:55 +0100 Subject: [PATCH 1/2] FO: Fix bug after removing product customization --- classes/CustomizationField.php | 1 + classes/Product.php | 20 +++++++++ controllers/front/CartController.php | 2 +- install-dev/data/db_structure.sql | 1 + install-dev/upgrade/sql/1.7.3.0.sql | 2 + src/Adapter/Product/AdminProductWrapper.php | 45 +++++++++++++++++---- 6 files changed, 62 insertions(+), 9 deletions(-) diff --git a/classes/CustomizationField.php b/classes/CustomizationField.php index 41cbed50f8b97..fbe2bef33cba8 100644 --- a/classes/CustomizationField.php +++ b/classes/CustomizationField.php @@ -54,6 +54,7 @@ class CustomizationFieldCore extends ObjectModel 'type' => array('type' => self::TYPE_INT, 'validate' => 'isUnsignedId', 'required' => true), 'required' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => true), 'is_module' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false), + 'is_deleted' => array('type' => self::TYPE_BOOL, 'validate' => 'isBool', 'required' => false), /* Lang fields */ 'name' => array('type' => self::TYPE_STRING, 'lang' => true, 'required' => true, 'size' => 255), diff --git a/classes/Product.php b/classes/Product.php index e646d046d724b..b33774fca5d0c 100644 --- a/classes/Product.php +++ b/classes/Product.php @@ -5188,6 +5188,7 @@ public function getCustomizationFields($id_lang = false, $id_shop = null) WHERE cf.`id_product` = '.(int)$this->id.($id_lang ? ' AND cfl.`id_lang` = '.(int)$id_lang : ''). ($id_shop ? ' AND cfl.`id_shop` = '.(int)$id_shop : ''). ($front ? ' AND !cf.`is_module`' : '').' + AND cf.`is_deleted` = 0 ORDER BY cf.`id_customization_field`')) { return false; } @@ -5204,6 +5205,25 @@ public function getCustomizationFields($id_lang = false, $id_shop = null) return $customization_fields; } + /** + * check if product has an activated and required customizationFields + * @return bool + * @throws \PrestaShopDatabaseException + */ + public function hasActivatedRequiredCustomizableFields(){ + if (!Customization::isFeatureActive()) { + return false; + } + + return (bool)Db::getInstance()->executeS(' + SELECT 1 + FROM `' . _DB_PREFIX_ . 'customization_field` + WHERE `id_product` = ' . (int)$this->id . ' + AND `required` = 1 + AND `is_deleted` = 0' + ); + } + public function getCustomizationFieldIds() { if (!Customization::isFeatureActive()) { diff --git a/controllers/front/CartController.php b/controllers/front/CartController.php index 1d917f9daf2bb..39edc5e82eb68 100644 --- a/controllers/front/CartController.php +++ b/controllers/front/CartController.php @@ -349,7 +349,7 @@ protected function processChangeProductInCart() } // Check customizable fields - if (!$product->hasAllRequiredCustomizableFields() && !$this->customization_id) { + if ($product->hasActivatedRequiredCustomizableFields() && !$this->customization_id) { $this->errors[] = $this->trans('Please fill in all of the required fields, and then save your customizations.', array(), 'Shop.Notifications.Error'); } diff --git a/install-dev/data/db_structure.sql b/install-dev/data/db_structure.sql index 6c43c03169311..ec49584a93a25 100644 --- a/install-dev/data/db_structure.sql +++ b/install-dev/data/db_structure.sql @@ -684,6 +684,7 @@ CREATE TABLE `PREFIX_customization_field` ( `type` tinyint(1) NOT NULL, `required` tinyint(1) NOT NULL, `is_module` TINYINT(1) NOT NULL DEFAULT '0', + `is_deleted` TINYINT(1) NOT NULL DEFAULT '0', PRIMARY KEY (`id_customization_field`), KEY `id_product` (`id_product`) ) ENGINE=ENGINE_TYPE DEFAULT CHARSET=utf8 COLLATION; diff --git a/install-dev/upgrade/sql/1.7.3.0.sql b/install-dev/upgrade/sql/1.7.3.0.sql index ff12ced89e147..a8b594a201641 100644 --- a/install-dev/upgrade/sql/1.7.3.0.sql +++ b/install-dev/upgrade/sql/1.7.3.0.sql @@ -36,3 +36,5 @@ CREATE TABLE IF NOT EXISTS `PREFIX_store_lang` ( ALTER TABLE `PREFIX_store` DROP `name`, DROP `address1`, DROP `address2`, DROP `hours`, DROP `note`; ALTER TABLE `PREFIX_feature_product` DROP PRIMARY KEY, ADD PRIMARY KEY (`id_feature`, `id_product`, `id_feature_value`); + +ALTER TABLE `PREFIX_customization_field` ADD `is_deleted` TINYINT(1) NOT NULL DEFAULT '0'; diff --git a/src/Adapter/Product/AdminProductWrapper.php b/src/Adapter/Product/AdminProductWrapper.php index 42899c5a19f5b..2a9fd6c73eb08 100644 --- a/src/Adapter/Product/AdminProductWrapper.php +++ b/src/Adapter/Product/AdminProductWrapper.php @@ -500,19 +500,48 @@ public function processProductCustomization($product, $data) $shopList = Shop::getContextListShopID(); + /** Update the customization fields to be deleted in the next step if not used */ + $updateQuery = 'UPDATE `' . _DB_PREFIX_ . 'customization_field` cf + SET cf.`is_deleted` = 1 + WHERE + cf.`id_product` = ' . (int)$product->id . ' + AND cf.`is_deleted` = 0 '; + if (!empty($customization_ids)) { + $updateQuery .= 'AND cf.`id_customization_field` NOT IN (' . implode(',', array_map('intval', $customization_ids)) . ')'; + } + Db::getInstance()->execute($updateQuery); + + $usedCustomizationIds = Db::getInstance()->executeS( + 'SELECT cd.`index` FROM `'._DB_PREFIX_.'customized_data` cd + LEFT JOIN `'._DB_PREFIX_.'customization_field` cf ON cf.`id_customization_field` = cd.`index` + WHERE cf.`id_product` = ' . (int)$product->id + ); + array_walk($usedCustomizationIds, create_function('&$v', '$v = (int)$v["index"];')); + $usedCustomizationIds = array_unique(array_merge($usedCustomizationIds, $customization_ids), SORT_REGULAR); + //remove customization field langs for current context shops - foreach ($product->getCustomizationFieldIds() as $customizationFiled) { + $productCustomization = $product->getCustomizationFieldIds(); + $toDeleteCustomizationIds = array(); + foreach ($productCustomization as $customizationFiled) { + if (!in_array((int)$customizationFiled['id_customization_field'], $usedCustomizationIds)) { + $toDeleteCustomizationIds[] = (int)$customizationFiled['id_customization_field']; + } //if the customization_field is still in use, only delete the current context shops langs, - //else delete all the langs - Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'customization_field_lang WHERE - `id_customization_field` = '.(int)$customizationFiled['id_customization_field']. - (in_array((int)$customizationFiled['id_customization_field'], $customization_ids) ? ' AND `id_shop` IN ('.implode(',', $shopList).')' : '')); + if (in_array((int)$customizationFiled['id_customization_field'], $customization_ids)) { + Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customization_field_lang` + WHERE `id_customization_field` = ' . (int)$customizationFiled['id_customization_field'] . ' + AND `id_shop` IN (' . implode(',', $shopList) . ')'); + } } //remove unused customization for the product - if (!empty($customization_ids)) { - Db::getInstance()->execute('DELETE FROM '._DB_PREFIX_.'customization_field WHERE - `id_product` = '.(int)$product->id.' AND `id_customization_field` NOT IN ('.implode(",", $customization_ids).')'); + if (!empty($toDeleteCustomizationIds)) { + $toDeleteIds = implode(",", $toDeleteCustomizationIds); + Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'customization_field` WHERE + `id_product` = '.(int)$product->id.' AND `id_customization_field` IN ('. $toDeleteIds .')'); + + Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'customization_field_lang` WHERE + `id_customization_field` IN (' . $toDeleteIds . ')'); } //create new customizations From 7d3d51742f9645df6a3aad73dbf25e1c055e6e24 Mon Sep 17 00:00:00 2001 From: Azouz-Jribi Date: Fri, 13 Oct 2017 15:25:56 +0100 Subject: [PATCH 2/2] CO: Extract the sql query from adapter class to model class --- classes/Customization.php | 21 +++++ classes/Product.php | 86 +++++++++++++++++++-- src/Adapter/Product/AdminProductWrapper.php | 35 ++------- 3 files changed, 107 insertions(+), 35 deletions(-) diff --git a/classes/Customization.php b/classes/Customization.php index 2004e0241933f..245ee34aed609 100644 --- a/classes/Customization.php +++ b/classes/Customization.php @@ -392,4 +392,25 @@ public function setWsCustomizedDataTextFields($values) return true; } + + /** + * Delete the current context shops langs + * + * @param int $idCustomizationField + * @param int[] $shopList + * @return bool + * @throws PrestaShopDatabaseException + */ + public static function deleteCustomizationFieldLangByShop($idCustomizationField, $shopList) + { + $return = Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customization_field_lang` + WHERE `id_customization_field` = ' . (int)$idCustomizationField . ' + AND `id_shop` IN (' . implode(',', $shopList) . ')'); + + if (!$return) { + throw new PrestaShopDatabaseException('An error occurred while deletion the customization fields lang'); + } + + return $return; + } } diff --git a/classes/Product.php b/classes/Product.php index b33774fca5d0c..daaa8f19113d4 100644 --- a/classes/Product.php +++ b/classes/Product.php @@ -5182,14 +5182,15 @@ public function getCustomizationFields($id_lang = false, $id_shop = null) $front = isset($context->controller->controller_type) && in_array($context->controller->controller_type, array('front')); if (!$result = Db::getInstance()->executeS(' - SELECT cf.`id_customization_field`, cf.`type`, cf.`required`, cfl.`name`, cfl.`id_lang` - FROM `'._DB_PREFIX_.'customization_field` cf - NATURAL JOIN `'._DB_PREFIX_.'customization_field_lang` cfl - WHERE cf.`id_product` = '.(int)$this->id.($id_lang ? ' AND cfl.`id_lang` = '.(int)$id_lang : ''). - ($id_shop ? ' AND cfl.`id_shop` = '.(int)$id_shop : ''). - ($front ? ' AND !cf.`is_module`' : '').' - AND cf.`is_deleted` = 0 - ORDER BY cf.`id_customization_field`')) { + SELECT cf.`id_customization_field`, cf.`type`, cf.`required`, cfl.`name`, cfl.`id_lang` + FROM `' . _DB_PREFIX_ . 'customization_field` cf + NATURAL JOIN `' . _DB_PREFIX_ . 'customization_field_lang` cfl + WHERE cf.`id_product` = ' . (int)$this->id . ($id_lang ? ' AND cfl.`id_lang` = ' . (int)$id_lang : '') . + ($id_shop ? ' AND cfl.`id_shop` = ' . (int)$id_shop : '') . + ($front ? ' AND !cf.`is_module`' : '') . ' + AND cf.`is_deleted` = 0 + ORDER BY cf.`id_customization_field`') + ) { return false; } @@ -6577,4 +6578,73 @@ public function getRedirectType() return false; } + + /** + * Return an array of customization fields IDs + * + * @return array|false + */ + public function getUsedCustomizationFieldsIds() + { + return Db::getInstance()->executeS( + 'SELECT cd.`index` FROM `' . _DB_PREFIX_ . 'customized_data` cd + LEFT JOIN `' . _DB_PREFIX_ . 'customization_field` cf ON cf.`id_customization_field` = cd.`index` + WHERE cf.`id_product` = ' . (int)$this->id + ); + } + + /** + * Remove unused customization for the product + * + * @param array $customizationIds - Array of customization fields IDs + * @return bool + * @throws PrestaShopDatabaseException + */ + public function deleteUnusedCustomizationFields($customizationIds) + { + $return = true; + if (is_array($customizationIds) && !empty($customizationIds)) { + $toDeleteIds = implode(",", $customizationIds); + $return &= Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customization_field` WHERE + `id_product` = ' . (int)$this->id . ' AND `id_customization_field` IN (' . $toDeleteIds . ')'); + + $return &= Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customization_field_lang` WHERE + `id_customization_field` IN (' . $toDeleteIds . ')'); + } + + if (!$return) { + throw new PrestaShopDatabaseException('An error occurred while deletion the customization fields'); + } + + return $return; + } + + /** + * Update the customization fields to be deleted if not used + * + * @param array $customizationIds - Array of excluded customization fields IDs + * @return bool + * @throws PrestaShopDatabaseException + */ + public function softDeleteCustomizationFields($customizationIds) + { + $return = true; + $updateQuery = 'UPDATE `' . _DB_PREFIX_ . 'customization_field` cf + SET cf.`is_deleted` = 1 + WHERE + cf.`id_product` = ' . (int)$this->id . ' + AND cf.`is_deleted` = 0 '; + + if (is_array($customizationIds) && !empty($customizationIds)) { + $updateQuery .= 'AND cf.`id_customization_field` NOT IN (' . implode(',', array_map('intval', $customizationIds)) . ')'; + } + + $return &= Db::getInstance()->execute($updateQuery); + + if (!$return) { + throw new PrestaShopDatabaseException('An error occurred while soft deletion the customization fields'); + } + + return $return; + } } diff --git a/src/Adapter/Product/AdminProductWrapper.php b/src/Adapter/Product/AdminProductWrapper.php index 2a9fd6c73eb08..f5cf04d4226cc 100644 --- a/src/Adapter/Product/AdminProductWrapper.php +++ b/src/Adapter/Product/AdminProductWrapper.php @@ -26,6 +26,7 @@ namespace PrestaShop\PrestaShop\Adapter\Product; use Attachment; +use PrestaShop\PrestaShop\Adapter\Entity\Customization; use SpecificPrice; use Customer; use Combination; @@ -501,22 +502,11 @@ public function processProductCustomization($product, $data) $shopList = Shop::getContextListShopID(); /** Update the customization fields to be deleted in the next step if not used */ - $updateQuery = 'UPDATE `' . _DB_PREFIX_ . 'customization_field` cf - SET cf.`is_deleted` = 1 - WHERE - cf.`id_product` = ' . (int)$product->id . ' - AND cf.`is_deleted` = 0 '; - if (!empty($customization_ids)) { - $updateQuery .= 'AND cf.`id_customization_field` NOT IN (' . implode(',', array_map('intval', $customization_ids)) . ')'; - } - Db::getInstance()->execute($updateQuery); - - $usedCustomizationIds = Db::getInstance()->executeS( - 'SELECT cd.`index` FROM `'._DB_PREFIX_.'customized_data` cd - LEFT JOIN `'._DB_PREFIX_.'customization_field` cf ON cf.`id_customization_field` = cd.`index` - WHERE cf.`id_product` = ' . (int)$product->id - ); - array_walk($usedCustomizationIds, create_function('&$v', '$v = (int)$v["index"];')); + $product->softDeleteCustomizationFields($customization_ids); + + $usedCustomizationIds = $product->getUsedCustomizationFieldsIds(); + $usedCustomizationIds = array_column($usedCustomizationIds, 'index'); + $usedCustomizationIds = array_map('intval', $usedCustomizationIds); $usedCustomizationIds = array_unique(array_merge($usedCustomizationIds, $customization_ids), SORT_REGULAR); //remove customization field langs for current context shops @@ -528,21 +518,12 @@ public function processProductCustomization($product, $data) } //if the customization_field is still in use, only delete the current context shops langs, if (in_array((int)$customizationFiled['id_customization_field'], $customization_ids)) { - Db::getInstance()->execute('DELETE FROM `' . _DB_PREFIX_ . 'customization_field_lang` - WHERE `id_customization_field` = ' . (int)$customizationFiled['id_customization_field'] . ' - AND `id_shop` IN (' . implode(',', $shopList) . ')'); + Customization::deleteCustomizationFieldLangByShop($customizationFiled['id_customization_field'], $shopList); } } //remove unused customization for the product - if (!empty($toDeleteCustomizationIds)) { - $toDeleteIds = implode(",", $toDeleteCustomizationIds); - Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'customization_field` WHERE - `id_product` = '.(int)$product->id.' AND `id_customization_field` IN ('. $toDeleteIds .')'); - - Db::getInstance()->execute('DELETE FROM `'._DB_PREFIX_.'customization_field_lang` WHERE - `id_customization_field` IN (' . $toDeleteIds . ')'); - } + $product->deleteUnusedCustomizationFields($toDeleteCustomizationIds); //create new customizations $countFieldText = 0;