Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tuyennn/77c3308b54b9abb7a907429c50166b6d to your computer and use it in GitHub Desktop.
Save tuyennn/77c3308b54b9abb7a907429c50166b6d to your computer and use it in GitHub Desktop.
MAGETWO-57153: [Backport] - [Github] Custom options not displayed correctly on a store view level
From 2b154f47cac22b48238714aaff57d86fff6ea3ac Mon Sep 17 00:00:00 2001
From: tuyennn <thinghost76@gmail.com>
Date: Wed, 14 Nov 2018 17:03:14 +0700
Subject: [PATCH] MAGETWO-57153: [Backport] - [Github] Custom options not
displayed correctly on a store view level #2908 #5885 #5612
---
.../Product/Initialization/Helper.php | 91 +++++++++++++++----
.../Model/Product/Option/Repository.php | 37 +++++++-
.../Model/Product/Option/SaveHandler.php | 20 +++-
.../Model/Product/Option/Validator/Select.php | 3 +
.../Model/Product/Option/Value.php | 2 +-
.../Model/ResourceModel/Product/Option.php | 43 +++++----
.../ResourceModel/Product/Option/Value.php | 7 ++
7 files changed, 156 insertions(+), 47 deletions(-)
diff --git a/vendor/magento/module-catalog/Controller/Adminhtml/Product/Initialization/Helper.php b/vendor/magento/module-catalog/Controller/Adminhtml/Product/Initialization/Helper.php
index 52addeb6d..9901697b4 100644
--- a/vendor/magento/module-catalog/Controller/Adminhtml/Product/Initialization/Helper.php
+++ b/vendor/magento/module-catalog/Controller/Adminhtml/Product/Initialization/Helper.php
@@ -75,6 +75,11 @@ class Helper
*/
private $dateTimeFilter;
+ /**
+ * @var \Magento\Catalog\Model\Product\LinkTypeProvider
+ */
+ private $linkTypeProvider;
+
/**
* Helper constructor.
* @param \Magento\Framework\App\RequestInterface $request
@@ -83,6 +88,7 @@ class Helper
* @param ProductLinks $productLinks
* @param \Magento\Backend\Helper\Js $jsHelper
* @param \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter
+ * @param \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider
*/
public function __construct(
\Magento\Framework\App\RequestInterface $request,
@@ -90,7 +96,8 @@ class Helper
StockDataFilter $stockFilter,
\Magento\Catalog\Model\Product\Initialization\Helper\ProductLinks $productLinks,
\Magento\Backend\Helper\Js $jsHelper,
- \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter
+ \Magento\Framework\Stdlib\DateTime\Filter\Date $dateFilter,
+ \Magento\Catalog\Model\Product\LinkTypeProvider $linkTypeProvider = null
) {
$this->request = $request;
$this->storeManager = $storeManager;
@@ -98,6 +105,8 @@ class Helper
$this->productLinks = $productLinks;
$this->jsHelper = $jsHelper;
$this->dateFilter = $dateFilter;
+ $this->linkTypeProvider = $linkTypeProvider ?: \Magento\Framework\App\ObjectManager::getInstance()
+ ->get(\Magento\Catalog\Model\Product\LinkTypeProvider::class);
}
/**
@@ -199,6 +208,10 @@ class Helper
$customOptions = [];
foreach ($options as $customOptionData) {
if (empty($customOptionData['is_delete'])) {
+ if (empty($customOptionData['option_id'])) {
+ $customOptionData['option_id'] = null;
+ }
+
if (isset($customOptionData['values'])) {
$customOptionData['values'] = array_filter($customOptionData['values'], function ($valueData) {
return empty($valueData['is_delete']);
@@ -206,7 +219,6 @@ class Helper
}
$customOption = $this->getCustomOptionFactory()->create(['data' => $customOptionData]);
$customOption->setProductSku($product->getSku());
- $customOption->setOptionId(null);
$customOptions[] = $customOption;
}
}
@@ -247,11 +259,17 @@ class Helper
$product = $this->productLinks->initializeLinks($product, $links);
$productLinks = $product->getProductLinks();
- $linkTypes = [
- 'related' => $product->getRelatedReadonly(),
- 'upsell' => $product->getUpsellReadonly(),
- 'crosssell' => $product->getCrosssellReadonly()
- ];
+ $linkTypes = [];
+
+ /** @var \Magento\Catalog\Api\Data\ProductLinkTypeInterface $linkTypeObject */
+ foreach ($this->linkTypeProvider->getItems() as $linkTypeObject) {
+ $linkTypes[$linkTypeObject->getName()] = $product->getData($linkTypeObject->getName() . '_readonly');
+ }
+
+ // skip linkTypes that were already processed on initializeLinks plugins
+ foreach ($productLinks as $productLink) {
+ unset($linkTypes[$productLink->getLinkType()]);
+ }
foreach ($linkTypes as $linkType => $readonly) {
if (isset($links[$linkType]) && !$readonly) {
@@ -310,26 +328,57 @@ class Helper
if (!is_array($productOptions)) {
return [];
}
-
if (!is_array($overwriteOptions)) {
return $productOptions;
}
-
- foreach ($productOptions as $index => $option) {
+ foreach ($productOptions as $optionIndex => $option) {
$optionId = $option['option_id'];
-
- if (!isset($overwriteOptions[$optionId])) {
- continue;
+ $option = $this->overwriteValue(
+ $optionId,
+ $option,
+ $overwriteOptions
+ );
+ if (isset($option['values']) && isset($overwriteOptions[$optionId]['values'])) {
+ foreach ($option['values'] as $valueIndex => $value) {
+ if (isset($value['option_type_id'])) {
+ $valueId = $value['option_type_id'];
+ $value = $this->overwriteValue(
+ $valueId,
+ $value,
+ $overwriteOptions[$optionId]['values']
+ );
+ $option['values'][$valueIndex] = $value;
+ }
+ }
}
+ $productOptions[$optionIndex] = $option;
+ }
+ return $productOptions;
+ }
+ /**
+ * Overwrite values of fields to default, if there are option id and field
+ * name in array overwriteOptions.
+ *
+ * @param int $optionId
+ * @param array $option
+ * @param array $overwriteOptions
+ *
+ * @return array
+ */
+ private function overwriteValue($optionId, $option, $overwriteOptions)
+ {
+ if (isset($overwriteOptions[$optionId])) {
foreach ($overwriteOptions[$optionId] as $fieldName => $overwrite) {
if ($overwrite && isset($option[$fieldName]) && isset($option['default_' . $fieldName])) {
- $productOptions[$index][$fieldName] = $option['default_' . $fieldName];
+ $option[$fieldName] = $option['default_' . $fieldName];
+ if ('title' == $fieldName) {
+ $option['is_delete_store_title'] = 1;
+ }
}
}
}
-
- return $productOptions;
+ return $option;
}
/**
@@ -339,8 +388,9 @@ class Helper
{
if (null === $this->customOptionFactory) {
$this->customOptionFactory = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory');
+ ->get(\Magento\Catalog\Api\Data\ProductCustomOptionInterfaceFactory::class);
}
+
return $this->customOptionFactory;
}
@@ -351,8 +401,9 @@ class Helper
{
if (null === $this->productLinkFactory) {
$this->productLinkFactory = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Catalog\Api\Data\ProductLinkInterfaceFactory');
+ ->get(\Magento\Catalog\Api\Data\ProductLinkInterfaceFactory::class);
}
+
return $this->productLinkFactory;
}
@@ -363,8 +414,9 @@ class Helper
{
if (null === $this->productRepository) {
$this->productRepository = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Catalog\Api\ProductRepositoryInterface\Proxy');
+ ->get('\Magento\Catalog\Api\ProductRepositoryInterface\Proxy');
}
+
return $this->productRepository;
}
@@ -377,6 +429,7 @@ class Helper
if (!is_object($this->linkResolver)) {
$this->linkResolver = ObjectManager::getInstance()->get(LinkResolver::class);
}
+
return $this->linkResolver;
}
diff --git a/vendor/magento/module-catalog/Model/Product/Option/Repository.php b/vendor/magento/module-catalog/Model/Product/Option/Repository.php
index 6f61de980..bd01532eb 100644
--- a/vendor/magento/module-catalog/Model/Product/Option/Repository.php
+++ b/vendor/magento/module-catalog/Model/Product/Option/Repository.php
@@ -133,14 +133,41 @@ class Repository implements \Magento\Catalog\Api\ProductCustomOptionRepositoryIn
*/
public function save(\Magento\Catalog\Api\Data\ProductCustomOptionInterface $option)
{
+ /** @var string $productSku */
$productSku = $option->getProductSku();
if (!$productSku) {
throw new CouldNotSaveException(__('ProductSku should be specified'));
}
+ /** @var \Magento\Catalog\Model\Product $product */
$product = $this->productRepository->get($productSku);
+ /** @var \Magento\Framework\EntityManager\EntityMetadataInterface $metadata */
$metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
$option->setData('product_id', $product->getData($metadata->getLinkField()));
- $option->setOptionId(null);
+ $option->setData('store_id', $product->getStoreId());
+ if ($option->getOptionId()) {
+ $options = $product->getOptions();
+ if (!$options) {
+ $options = $this->getProductOptions($product);
+ }
+ $persistedOption = array_filter(
+ $options,
+ function ($iOption) use ($option) {
+ return $option->getOptionId() == $iOption->getOptionId();
+ }
+ );
+ $persistedOption = reset($persistedOption);
+ if (!$persistedOption) {
+ throw new NoSuchEntityException();
+ }
+ /** @var array $originalValues */
+ $originalValues = $persistedOption->getValues();
+ /** @var array $newValues */
+ $newValues = $option->getData('values');
+ if ($newValues) {
+ $newValues = $this->markRemovedValues($newValues, $originalValues);
+ $option->setData('values', $newValues);
+ }
+ }
$option->save();
return $option;
}
@@ -203,7 +230,7 @@ class Repository implements \Magento\Catalog\Api\ProductCustomOptionRepositoryIn
{
if (null === $this->optionFactory) {
$this->optionFactory = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Catalog\Model\Product\OptionFactory');
+ ->get(\Magento\Catalog\Model\Product\OptionFactory::class);
}
return $this->optionFactory;
}
@@ -216,7 +243,7 @@ class Repository implements \Magento\Catalog\Api\ProductCustomOptionRepositoryIn
{
if (null === $this->collectionFactory) {
$this->collectionFactory = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory');
+ ->get(\Magento\Catalog\Model\ResourceModel\Product\Option\CollectionFactory::class);
}
return $this->collectionFactory;
}
@@ -229,7 +256,7 @@ class Repository implements \Magento\Catalog\Api\ProductCustomOptionRepositoryIn
{
if (null === $this->metadataPool) {
$this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Framework\EntityManager\MetadataPool');
+ ->get(\Magento\Framework\EntityManager\MetadataPool::class);
}
return $this->metadataPool;
}
@@ -242,7 +269,7 @@ class Repository implements \Magento\Catalog\Api\ProductCustomOptionRepositoryIn
{
if (null === $this->hydratorPool) {
$this->hydratorPool = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Framework\EntityManager\HydratorPool');
+ ->get(\Magento\Framework\EntityManager\HydratorPool::class);
}
return $this->hydratorPool;
}
diff --git a/vendor/magento/module-catalog/Model/Product/Option/SaveHandler.php b/vendor/magento/module-catalog/Model/Product/Option/SaveHandler.php
index cb089a95a..3ad234307 100644
--- a/vendor/magento/module-catalog/Model/Product/Option/SaveHandler.php
+++ b/vendor/magento/module-catalog/Model/Product/Option/SaveHandler.php
@@ -20,7 +20,6 @@ class SaveHandler implements ExtensionInterface
/**
* @param OptionRepository $optionRepository
- * @param MetadataPool $metadataPool
*/
public function __construct(
OptionRepository $optionRepository
@@ -36,12 +35,25 @@ class SaveHandler implements ExtensionInterface
*/
public function execute($entity, $arguments = [])
{
+ $options = $entity->getOptions();
+ $optionIds = [];
+ if ($options) {
+ $optionIds = array_map(
+ function ($option) {
+ /** @var \Magento\Catalog\Model\Product\Option $option */
+ return $option->getOptionId();
+ },
+ $options
+ );
+ }
/** @var \Magento\Catalog\Api\Data\ProductInterface $entity */
foreach ($this->optionRepository->getProductOptions($entity) as $option) {
- $this->optionRepository->delete($option);
+ if (!in_array($option->getOptionId(), $optionIds)) {
+ $this->optionRepository->delete($option);
+ }
}
- if ($entity->getOptions()) {
- foreach ($entity->getOptions() as $option) {
+ if ($options) {
+ foreach ($options as $option) {
$this->optionRepository->save($option);
}
}
diff --git a/vendor/magento/module-catalog/Model/Product/Option/Validator/Select.php b/vendor/magento/module-catalog/Model/Product/Option/Validator/Select.php
index 8dc506036..9b9dc4f25 100644
--- a/vendor/magento/module-catalog/Model/Product/Option/Validator/Select.php
+++ b/vendor/magento/module-catalog/Model/Product/Option/Validator/Select.php
@@ -51,6 +51,9 @@ class Select extends DefaultValidator
$storeId = $option->getProduct()->getStoreId();
}
foreach ($values as $value) {
+ if (isset($value['is_delete']) && (bool)$value['is_delete']) {
+ continue;
+ }
$type = isset($value['price_type']) ? $value['price_type'] : null;
$price = isset($value['price']) ? $value['price'] : null;
$title = isset($value['title']) ? $value['title'] : null;
diff --git a/vendor/magento/module-catalog/Model/Product/Option/Value.php b/vendor/magento/module-catalog/Model/Product/Option/Value.php
index 58566977a..c0b86bd2b 100644
--- a/vendor/magento/module-catalog/Model/Product/Option/Value.php
+++ b/vendor/magento/module-catalog/Model/Product/Option/Value.php
@@ -200,7 +200,7 @@ class Value extends AbstractModel implements \Magento\Catalog\Api\Data\ProductCu
'store_id',
$this->getOption()->getStoreId()
);
- $this->unsetData('option_type_id');
+
if ($this->getData('is_delete') == '1') {
if ($this->getId()) {
$this->deleteValues($this->getId());
diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Option.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Option.php
index 2fcfae56a..319afe2b6 100644
--- a/vendor/magento/module-catalog/Model/ResourceModel/Product/Option.php
+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Option.php
@@ -6,6 +6,7 @@
namespace Magento\Catalog\Model\ResourceModel\Product;
use Magento\Catalog\Api\Data\ProductInterface;
+use Magento\Store\Model\Store;
/**
* Catalog product custom option resource model
@@ -122,7 +123,7 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
$object->getId()
)->where(
'store_id = ?',
- \Magento\Store\Model\Store::DEFAULT_STORE_ID
+ Store::DEFAULT_STORE_ID
);
$optionId = $connection->fetchOne($statement);
@@ -139,7 +140,7 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
$data,
[
'option_id = ?' => $object->getId(),
- 'store_id = ?' => \Magento\Store\Model\Store::DEFAULT_STORE_ID
+ 'store_id = ?' => Store::DEFAULT_STORE_ID
]
);
} else {
@@ -147,7 +148,7 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
new \Magento\Framework\DataObject(
[
'option_id' => $object->getId(),
- 'store_id' => \Magento\Store\Model\Store::DEFAULT_STORE_ID,
+ 'store_id' => Store::DEFAULT_STORE_ID,
'price' => $object->getPrice(),
'price_type' => $object->getPriceType(),
]
@@ -159,11 +160,11 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
}
$scope = (int)$this->_config->getValue(
- \Magento\Store\Model\Store::XML_PATH_PRICE_SCOPE,
+ Store::XML_PATH_PRICE_SCOPE,
\Magento\Store\Model\ScopeInterface::SCOPE_STORE
);
- if ($object->getStoreId() != '0' && $scope == \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE) {
+ if ($object->getStoreId() != '0' && $scope == Store::PRICE_SCOPE_WEBSITE) {
$baseCurrency = $this->_config->getValue(
\Magento\Directory\Model\Currency::XML_PATH_CURRENCY_BASE,
'default'
@@ -222,7 +223,7 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
}
}
}
- } elseif ($scope == \Magento\Store\Model\Store::PRICE_SCOPE_WEBSITE && $object->getData('scope', 'price')
+ } elseif ($scope == Store::PRICE_SCOPE_WEBSITE && $object->getData('scope', 'price')
) {
$connection->delete(
$priceTable,
@@ -245,16 +246,17 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
$connection = $this->getConnection();
$titleTableName = $this->getTable('catalog_product_option_title');
- foreach ([\Magento\Store\Model\Store::DEFAULT_STORE_ID, $object->getStoreId()] as $storeId) {
+ foreach ([Store::DEFAULT_STORE_ID, $object->getStoreId()] as $storeId) {
$existInCurrentStore = $this->getColFromOptionTable($titleTableName, (int)$object->getId(), (int)$storeId);
- $existInDefaultStore = $this->getColFromOptionTable(
- $titleTableName,
- (int)$object->getId(),
- \Magento\Store\Model\Store::DEFAULT_STORE_ID
- );
+ $existInDefaultStore = (int)$storeId == Store::DEFAULT_STORE_ID ?
+ $existInCurrentStore :
+ $this->getColFromOptionTable($titleTableName, (int)$object->getId(), Store::DEFAULT_STORE_ID);
if ($object->getTitle()) {
+ $isDeleteStoreTitle = (bool)$object->getData('is_delete_store_title');
if ($existInCurrentStore) {
- if ($object->getStoreId() == $storeId) {
+ if ($isDeleteStoreTitle && (int)$storeId != Store::DEFAULT_STORE_ID) {
+ $connection->delete($titleTableName, ['option_title_id = ?' => $existInCurrentStore]);
+ } elseif ($object->getStoreId() == $storeId) {
$data = $this->_prepareDataForTable(
new \Magento\Framework\DataObject(['title' => $object->getTitle()]),
$titleTableName
@@ -270,8 +272,12 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
}
} else {
// we should insert record into not default store only of if it does not exist in default store
- if (($storeId == \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInDefaultStore)
- || ($storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID && !$existInCurrentStore)
+ if (($storeId == Store::DEFAULT_STORE_ID && !$existInDefaultStore) ||
+ (
+ $storeId != Store::DEFAULT_STORE_ID &&
+ !$existInCurrentStore &&
+ !$isDeleteStoreTitle
+ )
) {
$data = $this->_prepareDataForTable(
new \Magento\Framework\DataObject(
@@ -287,7 +293,7 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
}
}
} else {
- if ($object->getId() && $object->getStoreId() > \Magento\Store\Model\Store::DEFAULT_STORE_ID
+ if ($object->getId() && $object->getStoreId() > Store::DEFAULT_STORE_ID
&& $storeId
) {
$connection->delete(
@@ -513,7 +519,7 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
'option_title_default.option_type_id=option_type.option_type_id',
$connection->quoteInto(
'option_title_default.store_id = ?',
- \Magento\Store\Model\Store::DEFAULT_STORE_ID
+ Store::DEFAULT_STORE_ID
)
]
);
@@ -567,8 +573,9 @@ class Option extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
{
if (null === $this->metadataPool) {
$this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
- ->get('Magento\Framework\EntityManager\MetadataPool');
+ ->get(\Magento\Framework\EntityManager\MetadataPool::class);
}
+
return $this->metadataPool;
}
}
diff --git a/vendor/magento/module-catalog/Model/ResourceModel/Product/Option/Value.php b/vendor/magento/module-catalog/Model/ResourceModel/Product/Option/Value.php
index a3df7bbc0..1829f232c 100644
--- a/vendor/magento/module-catalog/Model/ResourceModel/Product/Option/Value.php
+++ b/vendor/magento/module-catalog/Model/ResourceModel/Product/Option/Value.php
@@ -237,6 +237,13 @@ class Value extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
);
$optionTypeId = $this->getConnection()->fetchOne($select);
$existInCurrentStore = $this->getOptionIdFromOptionTable($titleTable, (int)$object->getId(), (int)$storeId);
+
+ if ($storeId != \Magento\Store\Model\Store::DEFAULT_STORE_ID &&
+ $object->getData('is_delete_store_title')
+ ) {
+ $object->unsetData('title');
+ }
+
if ($object->getTitle()) {
if ($existInCurrentStore) {
if ($storeId == $object->getStoreId()) {
--
2.19.1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment