Skip to content

Commit 779058f

Browse files
committed
AC-2904-v1::Saving product with non-default store scope causes untouched attributes to become store scoped if loaded using ProductRepository
1 parent aed0f3d commit 779058f

File tree

1 file changed

+35
-42
lines changed

1 file changed

+35
-42
lines changed

app/code/Magento/Catalog/Model/ProductRepository.php

Lines changed: 35 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Magento\Catalog\Api\Data\ProductAttributeInterface;
1111
use Magento\Catalog\Api\Data\ProductExtension;
1212
use Magento\Catalog\Api\Data\ProductInterface;
13+
use Magento\Catalog\Controller\Adminhtml\Product\Initialization\Helper\AttributeFilter;
1314
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
1415
use Magento\Catalog\Model\Product\Gallery\MimeTypeExtensionMap;
1516
use Magento\Catalog\Model\ProductRepository\MediaGalleryProcessor;
@@ -187,6 +188,11 @@ class ProductRepository implements \Magento\Catalog\Api\ProductRepositoryInterfa
187188
*/
188189
private $scopeOverriddenValue;
189190

191+
/**
192+
* @var AttributeFilter
193+
*/
194+
private $attributeFilter;
195+
190196
/**
191197
* ProductRepository constructor.
192198
* @param ProductFactory $productFactory
@@ -237,6 +243,7 @@ public function __construct(
237243
MimeTypeExtensionMap $mimeTypeExtensionMap,
238244
ImageProcessorInterface $imageProcessor,
239245
\Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $extensionAttributesJoinProcessor,
246+
AttributeFilter $attributeFilter = null,
240247
CollectionProcessorInterface $collectionProcessor = null,
241248
\Magento\Framework\Serialize\Serializer\Json $serializer = null,
242249
$cacheLimit = 1000,
@@ -260,6 +267,8 @@ public function __construct(
260267
$this->contentFactory = $contentFactory;
261268
$this->imageProcessor = $imageProcessor;
262269
$this->extensionAttributesJoinProcessor = $extensionAttributesJoinProcessor;
270+
$this->attributeFilter = $attributeFilter ?: \Magento\Framework\App\ObjectManager::getInstance()
271+
->get(AttributeFilter::class);
263272
$this->collectionProcessor = $collectionProcessor ?: $this->getCollectionProcessor();
264273
$this->serializer = $serializer ?: \Magento\Framework\App\ObjectManager::getInstance()
265274
->get(\Magento\Framework\Serialize\Serializer\Json::class);
@@ -597,38 +606,8 @@ public function save(ProductInterface $product, $saveOptions = false)
597606
&& $product->getStoreId() !== Store::DEFAULT_STORE_ID
598607
&& (count($stores) > 1 || count($websites) === 1)
599608
) {
600-
foreach ($productAttributes as $attribute) {
601-
$attributeCode = $attribute->getAttributeCode();
602-
$value = $product->getData($attributeCode);
603-
if ($existingProduct->getData($attributeCode) === $value
604-
&& $attribute->getScope() !== EavAttributeInterface::SCOPE_GLOBAL_TEXT
605-
&& !is_array($value)
606-
&& !$attribute->isStatic()
607-
&& !array_key_exists($attributeCode, $productDataToChange)
608-
&& $value !== null
609-
&& !$this->scopeOverriddenValue->containsValue(
610-
ProductInterface::class,
611-
$product,
612-
$attributeCode,
613-
$product->getStoreId()
614-
)
615-
) {
616-
$product->setData(
617-
$attributeCode,
618-
$attributeCode === ProductAttributeInterface::CODE_SEO_FIELD_URL_KEY ? false : null
619-
);
620-
$hasDataChanged = true;
621-
}
622-
}
623-
$storeScopedAttributes = [
624-
ProductAttributeInterface::CODE_SEO_FIELD_META_TITLE,
625-
ProductAttributeInterface::CODE_SEO_FIELD_META_DESCRIPTION,
626-
ProductAttributeInterface::CODE_SEO_FIELD_META_KEYWORD,
627-
];
628-
$origDataAttributes = [
629-
ProductAttributeInterface::CODE_NAME,
630-
ProductAttributeInterface::CODE_SEO_FIELD_URL_KEY,
631-
];
609+
610+
$useDefault = [];
632611
foreach ($product->getAttributes() as $attribute) {
633612
$defaultValue = $attribute->getDefaultValue();
634613
$attributeCode = $attribute->getAttributeCode();
@@ -644,29 +623,43 @@ public function save(ProductInterface $product, $saveOptions = false)
644623
$product->getStoreId()
645624
)
646625
) {
647-
$product->setData($attributeCode);
626+
$useDefault[$attributeCode] = '1';
648627
$hasDataChanged = true;
649628
} elseif (!$defaultValue && $value !== null
650-
&& $attribute->getScope() === EavAttributeInterface::SCOPE_STORE_TEXT
629+
&& $attribute->getScope() !== EavAttributeInterface::SCOPE_GLOBAL_TEXT
651630
&& $existingProduct->getData($attributeCode) === $value
652-
&& in_array($attributeCode, $storeScopedAttributes)
653-
) {
654-
$product->setData($attributeCode);
655-
$hasDataChanged = true;
656-
} elseif (in_array($attributeCode, $origDataAttributes)
657-
&& $existingProduct->getOrigData($attributeCode) === $value
631+
&& !$this->scopeOverriddenValue->containsValue(
632+
ProductInterface::class,
633+
$product,
634+
$attributeCode,
635+
$product->getStoreId()
636+
)
658637
) {
659-
$product->setData($attributeCode);
638+
$useDefault[$attributeCode] = '1';
660639
$hasDataChanged = true;
640+
} else {
641+
$useDefault[$attributeCode] = '0';
661642
}
643+
662644
}
663645
if ($hasDataChanged) {
664646
$product->setData('_edit_mode', true);
665647
}
666648
}
667649
}
650+
$productDataArray = $this->extensibleDataObjectConverter
651+
->toNestedArray($product, [], ProductInterface::class);
652+
$productDataArray = array_replace($productDataArray, $product->getData());
653+
654+
$productDataArray = $this->attributeFilter->prepareProductAttributes(
655+
$product,
656+
$productDataArray,
657+
$useDefault
658+
);
659+
$newProduct = $this->productFactory->create();
660+
$newProduct->setData($productDataArray);
661+
$this->saveProduct($newProduct);
668662

669-
$this->saveProduct($product);
670663
if ($assignToCategories === true && $product->getCategoryIds()) {
671664
$this->linkManagement->assignProductToCategories(
672665
$product->getSku(),

0 commit comments

Comments
 (0)