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