8
8
use Magento\Catalog\Api\Data\ProductInterface;
9
9
use Magento\Catalog\Model\Attribute\ScopeOverriddenValue;
10
10
use Magento\Catalog\Model\Product;
11
+ use Magento\Catalog\Model\Product\Option\Repository as OptionRepository;
11
12
use Magento\Catalog\Model\ProductFactory;
13
+ use Magento\Framework\App\ObjectManager;
14
+ use Magento\Framework\EntityManager\MetadataPool;
15
+ use Magento\UrlRewrite\Model\Exception\UrlAlreadyExistsException;
12
16
13
17
/**
14
18
* Catalog product copier.
@@ -35,9 +39,10 @@ class Copier
35
39
protected $productFactory;
36
40
37
41
/**
38
- * @var \Magento\Framework\EntityManager\ MetadataPool
42
+ * @var MetadataPool
39
43
*/
40
44
protected $metadataPool;
45
+
41
46
/**
42
47
* @var ScopeOverriddenValue
43
48
*/
@@ -47,30 +52,38 @@ class Copier
47
52
* @param CopyConstructorInterface $copyConstructor
48
53
* @param ProductFactory $productFactory
49
54
* @param ScopeOverriddenValue $scopeOverriddenValue
55
+ * @param OptionRepository|null $optionRepository
56
+ * @param MetadataPool|null $metadataPool
50
57
*/
51
58
public function __construct(
52
59
CopyConstructorInterface $copyConstructor,
53
60
ProductFactory $productFactory,
54
- ScopeOverriddenValue $scopeOverriddenValue
61
+ ScopeOverriddenValue $scopeOverriddenValue,
62
+ OptionRepository $optionRepository = null,
63
+ MetadataPool $metadataPool = null
55
64
) {
56
65
$this->productFactory = $productFactory;
57
66
$this->copyConstructor = $copyConstructor;
58
67
$this->scopeOverriddenValue = $scopeOverriddenValue;
68
+ $this->optionRepository = $optionRepository ?: ObjectManager::getInstance()->get(OptionRepository::class);
69
+ $this->metadataPool = $metadataPool ?: ObjectManager::getInstance()->get(MetadataPool::class);
59
70
}
60
71
61
72
/**
62
73
* Create product duplicate
63
74
*
64
75
* @param \Magento\Catalog\Model\Product $product
76
+ *
65
77
* @return \Magento\Catalog\Model\Product
78
+ *
79
+ * @throws \Exception
66
80
*/
67
81
public function copy(Product $product)
68
82
{
69
83
$product->getWebsiteIds();
70
84
$product->getCategoryIds();
71
85
72
- /** @var \Magento\Framework\EntityManager\EntityMetadataInterface $metadata */
73
- $metadata = $this->getMetadataPool()->getMetadata(ProductInterface::class);
86
+ $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
74
87
75
88
/** @var \Magento\Catalog\Model\Product $duplicate */
76
89
$duplicate = $this->productFactory->create();
@@ -88,7 +101,7 @@ public function copy(Product $product)
88
101
$this->copyConstructor->build($product, $duplicate);
89
102
$this->setDefaultUrl($product, $duplicate);
90
103
$this->setStoresUrl($product, $duplicate);
91
- $this->getOptionRepository() ->duplicate($product, $duplicate);
104
+ $this->optionRepository ->duplicate($product, $duplicate);
92
105
$product->getResource()->duplicate(
93
106
$product->getData($metadata->getLinkField()),
94
107
$duplicate->getData($metadata->getLinkField())
@@ -123,13 +136,16 @@ private function setDefaultUrl(Product $product, Product $duplicate) : void
123
136
*
124
137
* @param Product $product
125
138
* @param Product $duplicate
139
+ *
126
140
* @return void
141
+ * @throws UrlAlreadyExistsException
127
142
*/
128
143
private function setStoresUrl(Product $product, Product $duplicate) : void
129
144
{
130
145
$storeIds = $duplicate->getStoreIds();
131
146
$productId = $product->getId();
132
147
$productResource = $product->getResource();
148
+ $attribute = $productResource->getAttribute('url_key');
133
149
$duplicate->setData('save_rewrites_history', false);
134
150
foreach ($storeIds as $storeId) {
135
151
$useDefault = !$this->scopeOverriddenValue->containsValue(
@@ -141,20 +157,23 @@ private function setStoresUrl(Product $product, Product $duplicate) : void
141
157
if ($useDefault) {
142
158
continue;
143
159
}
144
- $isDuplicateSaved = false;
160
+
145
161
$duplicate->setStoreId($storeId);
146
162
$urlKey = $productResource->getAttributeRawValue($productId, 'url_key', $storeId);
163
+ $iteration = 0;
164
+
147
165
do {
166
+ if ($iteration === 10) {
167
+ throw new UrlAlreadyExistsException();
168
+ }
169
+
148
170
$urlKey = $this->modifyUrl($urlKey);
149
171
$duplicate->setUrlKey($urlKey);
150
- $duplicate->setData('url_path', null);
151
- try {
152
- $duplicate->save();
153
- $isDuplicateSaved = true;
154
- // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock
155
- } catch (\Magento\Framework\Exception\AlreadyExistsException $e) {
156
- }
157
- } while (!$isDuplicateSaved);
172
+ $iteration++;
173
+ } while (!$attribute->getEntity()->checkAttributeUniqueValue($attribute, $duplicate));
174
+ $duplicate->setData('url_path', null);
175
+ $productResource->saveAttribute($duplicate, 'url_path');
176
+ $productResource->saveAttribute($duplicate, 'url_key');
158
177
}
159
178
$duplicate->setStoreId(\Magento\Store\Model\Store::DEFAULT_STORE_ID);
160
179
}
@@ -168,38 +187,8 @@ private function setStoresUrl(Product $product, Product $duplicate) : void
168
187
private function modifyUrl(string $urlKey) : string
169
188
{
170
189
return preg_match('/(.*)-(\d+)$/', $urlKey, $matches)
171
- ? $matches[1] . '-' . ($matches[2] + 1)
172
- : $urlKey . '-1';
173
- }
174
-
175
- /**
176
- * Returns product option repository.
177
- *
178
- * @return Option\Repository
179
- * @deprecated 101.0.0
180
- */
181
- private function getOptionRepository()
182
- {
183
- if (null === $this->optionRepository) {
184
- $this->optionRepository = \Magento\Framework\App\ObjectManager::getInstance()
185
- ->get(\Magento\Catalog\Model\Product\Option\Repository::class);
186
- }
187
- return $this->optionRepository;
188
- }
189
-
190
- /**
191
- * Returns metadata pool.
192
- *
193
- * @return \Magento\Framework\EntityManager\MetadataPool
194
- * @deprecated 101.0.0
195
- */
196
- private function getMetadataPool()
197
- {
198
- if (null === $this->metadataPool) {
199
- $this->metadataPool = \Magento\Framework\App\ObjectManager::getInstance()
200
- ->get(\Magento\Framework\EntityManager\MetadataPool::class);
201
- }
202
- return $this->metadataPool;
190
+ ? $matches[1] . '-' . ($matches[2] + 1)
191
+ : $urlKey . '-1';
203
192
}
204
193
205
194
/**
0 commit comments