Skip to content

Commit b5c9681

Browse files
committed
Merge remote-tracking branch 'magento-mpi/MC-21882' into MPI-PR-2019-10-18
2 parents 47a8826 + 9c47fba commit b5c9681

File tree

6 files changed

+173
-24
lines changed

6 files changed

+173
-24
lines changed

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

Lines changed: 5 additions & 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

@@ -110,6 +110,8 @@ public function getDefaultValues($entityType, $entity)
110110
}
111111

112112
/**
113+
* Init attribute values.
114+
*
113115
* @param string $entityType
114116
* @param \Magento\Catalog\Model\AbstractModel $entity
115117
* @param int $storeId
@@ -158,6 +160,8 @@ private function initAttributeValues($entityType, $entity, $storeId)
158160
}
159161

160162
/**
163+
* Returns entity attributes.
164+
*
161165
* @param string $entityType
162166
* @return \Magento\Eav\Api\Data\AttributeInterface[]
163167
*/

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: 28 additions & 12 deletions
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,15 +74,20 @@ 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

75-
$this->setProperties($this->_model, [
76-
'optionRepository' => $this->optionRepositoryMock,
77-
'metadataPool' => $metadataPool,
78-
]);
84+
$this->setProperties(
85+
$this->_model,
86+
[
87+
'optionRepository' => $this->optionRepositoryMock,
88+
'metadataPool' => $metadataPool,
89+
]
90+
);
7991
}
8092

8193
/**
@@ -103,10 +115,12 @@ public function testCopy()
103115
];
104116
$this->productMock->expects($this->atLeastOnce())->method('getWebsiteIds');
105117
$this->productMock->expects($this->atLeastOnce())->method('getCategoryIds');
106-
$this->productMock->expects($this->any())->method('getData')->willReturnMap([
107-
['', null, $productData],
108-
['linkField', null, '1'],
109-
]);
118+
$this->productMock->expects($this->any())->method('getData')->willReturnMap(
119+
[
120+
['', null, $productData],
121+
['linkField', null, '1'],
122+
]
123+
);
110124

111125
$entityMock = $this->getMockForAbstractClass(
112126
\Magento\Eav\Model\Entity\AbstractEntity::class,
@@ -191,9 +205,11 @@ public function testCopy()
191205

192206
$this->metadata->expects($this->any())->method('getLinkField')->willReturn('linkField');
193207

194-
$duplicateMock->expects($this->any())->method('getData')->willReturnMap([
195-
['linkField', null, '2'],
196-
]);
208+
$duplicateMock->expects($this->any())->method('getData')->willReturnMap(
209+
[
210+
['linkField', null, '2'],
211+
]
212+
);
197213
$this->optionRepositoryMock->expects($this->once())
198214
->method('duplicate')
199215
->with($this->productMock, $duplicateMock);
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)