Skip to content

Commit 9f5c8ad

Browse files
committed
MC-21882: Product url_key not updating on duplicating
- Fix a case when url_key for store view is equal to value from default store
1 parent abbfa03 commit 9f5c8ad

File tree

6 files changed

+151
-13
lines changed

6 files changed

+151
-13
lines changed

app/code/Magento/Catalog/Model/Attribute/ScopeOverriddenValue.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ public function containsValue($entityType, $entity, $attributeCode, $storeId)
8181
if ((int)$storeId === Store::DEFAULT_STORE_ID) {
8282
return false;
8383
}
84-
if ($this->attributesValues === null) {
84+
if (!isset($this->attributesValues[$storeId])) {
8585
$this->initAttributeValues($entityType, $entity, (int)$storeId);
8686
}
8787

app/code/Magento/Catalog/Model/Product/Copier.php

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
namespace Magento\Catalog\Model\Product;
77

88
use Magento\Catalog\Api\Data\ProductInterface;
9+
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
910
use Magento\Catalog\Model\Product;
11+
use Magento\Catalog\Model\ProductFactory;
1012

1113
/**
1214
* Catalog product copier.
@@ -28,25 +30,32 @@ class Copier
2830
protected $copyConstructor;
2931

3032
/**
31-
* @var \Magento\Catalog\Model\ProductFactory
33+
* @var ProductFactory
3234
*/
3335
protected $productFactory;
3436

3537
/**
3638
* @var \Magento\Framework\EntityManager\MetadataPool
3739
*/
3840
protected $metadataPool;
41+
/**
42+
* @var ScopeOverriddenValue
43+
*/
44+
private $scopeOverriddenValue;
3945

4046
/**
4147
* @param CopyConstructorInterface $copyConstructor
42-
* @param \Magento\Catalog\Model\ProductFactory $productFactory
48+
* @param ProductFactory $productFactory
49+
* @param ScopeOverriddenValue $scopeOverriddenValue
4350
*/
4451
public function __construct(
4552
CopyConstructorInterface $copyConstructor,
46-
\Magento\Catalog\Model\ProductFactory $productFactory
53+
ProductFactory $productFactory,
54+
ScopeOverriddenValue $scopeOverriddenValue
4755
) {
4856
$this->productFactory = $productFactory;
4957
$this->copyConstructor = $copyConstructor;
58+
$this->scopeOverriddenValue = $scopeOverriddenValue;
5059
}
5160

5261
/**
@@ -121,19 +130,20 @@ private function setStoresUrl(Product $product, Product $duplicate) : void
121130
$storeIds = $duplicate->getStoreIds();
122131
$productId = $product->getId();
123132
$productResource = $product->getResource();
124-
$defaultUrlKey = $productResource->getAttributeRawValue(
125-
$productId,
126-
'url_key',
127-
\Magento\Store\Model\Store::DEFAULT_STORE_ID
128-
);
129133
$duplicate->setData('save_rewrites_history', false);
130134
foreach ($storeIds as $storeId) {
135+
$useDefault = !$this->scopeOverriddenValue->containsValue(
136+
ProductInterface::class,
137+
$product,
138+
'url_key',
139+
$storeId
140+
);
141+
if ($useDefault) {
142+
continue;
143+
}
131144
$isDuplicateSaved = false;
132145
$duplicate->setStoreId($storeId);
133146
$urlKey = $productResource->getAttributeRawValue($productId, 'url_key', $storeId);
134-
if ($urlKey === $defaultUrlKey) {
135-
continue;
136-
}
137147
do {
138148
$urlKey = $this->modifyUrl($urlKey);
139149
$duplicate->setUrlKey($urlKey);

app/code/Magento/Catalog/Test/Unit/Model/Product/CopierTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\Catalog\Test\Unit\Model\Product;
77

88
use Magento\Catalog\Api\Data\ProductInterface;
9+
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
910
use Magento\Catalog\Model\Product;
1011
use Magento\Catalog\Model\Product\Copier;
1112

@@ -46,6 +47,11 @@ class CopierTest extends \PHPUnit\Framework\TestCase
4647
*/
4748
protected $metadata;
4849

50+
/**
51+
* @var ScopeOverriddenValue|\PHPUnit_Framework_MockObject_MockObject
52+
*/
53+
private $scopeOverriddenValue;
54+
4955
protected function setUp()
5056
{
5157
$this->copyConstructorMock = $this->createMock(\Magento\Catalog\Model\Product\CopyConstructorInterface::class);
@@ -59,6 +65,7 @@ protected function setUp()
5965
$this->optionRepositoryMock;
6066
$this->productMock = $this->createMock(Product::class);
6167
$this->productMock->expects($this->any())->method('getEntityId')->willReturn(1);
68+
$this->scopeOverriddenValue = $this->createMock(ScopeOverriddenValue::class);
6269

6370
$this->metadata = $this->getMockBuilder(\Magento\Framework\EntityManager\EntityMetadata::class)
6471
->disableOriginalConstructor()
@@ -67,9 +74,11 @@ protected function setUp()
6774
->disableOriginalConstructor()
6875
->getMock();
6976
$metadataPool->expects($this->any())->method('getMetadata')->willReturn($this->metadata);
77+
7078
$this->_model = new Copier(
7179
$this->copyConstructorMock,
72-
$this->productFactoryMock
80+
$this->productFactoryMock,
81+
$this->scopeOverriddenValue
7382
);
7483

7584
$this->setProperties($this->_model, [
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Product;
9+
10+
use Magento\Catalog\Model\ProductRepository;
11+
use Magento\Store\Model\Store;
12+
use Magento\TestFramework\Helper\Bootstrap;
13+
use PHPUnit\Framework\TestCase;
14+
15+
/**
16+
* Tests product copier.
17+
*/
18+
class CopierTest extends TestCase
19+
{
20+
/**
21+
* Tests copying of product.
22+
*
23+
* Case when url_key is set for store view and has equal value to default store.
24+
*
25+
* @magentoDataFixture Magento/Catalog/_files/product_simple_multistore_with_url_key.php
26+
* @magentoAppArea adminhtml
27+
*/
28+
public function testProductCopyWithExistingUrlKey()
29+
{
30+
$productSKU = 'simple_100';
31+
/** @var ProductRepository $productRepository */
32+
$productRepository = Bootstrap::getObjectManager()->get(ProductRepository::class);
33+
$copier = Bootstrap::getObjectManager()->get(Copier::class);
34+
35+
$product = $productRepository->get($productSKU);
36+
$duplicate = $copier->copy($product);
37+
38+
$duplicateStoreView = $productRepository->getById($duplicate->getId(), false, Store::DISTRO_STORE_ID);
39+
$productStoreView = $productRepository->get($productSKU, false, Store::DISTRO_STORE_ID);
40+
41+
$this->assertNotEquals(
42+
$duplicateStoreView->getUrlKey(),
43+
$productStoreView->getUrlKey(),
44+
'url_key of product duplicate should be different then url_key of the product for the same store view'
45+
);
46+
}
47+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Catalog\Model\Product;
10+
use Magento\Catalog\Model\Product\Attribute\Source\Status;
11+
use Magento\Catalog\Model\Product\Type;
12+
use Magento\Catalog\Model\Product\Visibility;
13+
use Magento\CatalogInventory\Api\Data\StockItemInterface;
14+
use Magento\Store\Model\Store;
15+
use Magento\TestFramework\Helper\Bootstrap;
16+
use Magento\TestFramework\ObjectManager;
17+
18+
/** @var ObjectManager $objectManager */
19+
$objectManager = Bootstrap::getObjectManager();
20+
21+
/** @var Product $product */
22+
$product = $objectManager->create(Product::class);
23+
$product->isObjectNew(true);
24+
$product->setTypeId(Type::TYPE_SIMPLE)
25+
->setStoreId(Store::DEFAULT_STORE_ID)
26+
->setAttributeSetId(4)
27+
->setWebsiteIds([1])
28+
->setName('Simple Product 100')
29+
->setSku('simple_100')
30+
->setUrlKey('url-key')
31+
->setPrice(10)
32+
->setWeight(1)
33+
->setVisibility(Visibility::VISIBILITY_BOTH)
34+
->setStatus(Status::STATUS_ENABLED);
35+
36+
/** @var StockItemInterface $stockItem */
37+
$stockItem = $objectManager->create(StockItemInterface::class);
38+
$stockItem->setQty(100)
39+
->setIsInStock(true);
40+
$extensionAttributes = $product->getExtensionAttributes();
41+
$extensionAttributes->setStockItem($stockItem);
42+
43+
/** @var ProductRepositoryInterface $productRepository */
44+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
45+
$product = $productRepository->save($product);
46+
47+
$product->setStoreId(Store::DISTRO_STORE_ID)
48+
->setName('StoreTitle')
49+
->setUrlKey('url-key');
50+
$productRepository->save($product);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
use Magento\Catalog\Api\ProductRepositoryInterface;
9+
use Magento\Framework\Exception\NoSuchEntityException;
10+
use Magento\TestFramework\Helper\Bootstrap;
11+
use Magento\TestFramework\ObjectManager;
12+
13+
/** @var ObjectManager $objectManager */
14+
$objectManager = Bootstrap::getObjectManager();
15+
16+
/** @var ProductRepositoryInterface $productRepository */
17+
$productRepository = $objectManager->get(ProductRepositoryInterface::class);
18+
try {
19+
$productRepository->deleteById('simple_100');
20+
} catch (NoSuchEntityException $e) {
21+
//Entity already deleted
22+
}

0 commit comments

Comments
 (0)