33 * Copyright © Magento, Inc. All rights reserved.
44 * See COPYING.txt for license details.
55 */
6+ declare (strict_types=1 );
7+
68namespace Magento \CatalogUrlRewrite \Observer ;
79
10+ use Magento \Catalog \Model \Product ;
11+ use Magento \Catalog \Model \Category ;
12+ use Magento \Catalog \Model \ResourceModel \Product \Collection ;
13+ use Magento \Catalog \Model \ResourceModel \Product \CollectionFactory ;
14+ use Magento \CatalogUrlRewrite \Model \Category \ChildrenCategoriesProvider ;
15+ use Magento \CatalogUrlRewrite \Model \CategoryProductUrlPathGenerator ;
16+ use Magento \CatalogUrlRewrite \Model \CategoryUrlRewriteGenerator ;
17+ use Magento \CatalogUrlRewrite \Model \ProductScopeRewriteGenerator ;
18+ use Magento \CatalogUrlRewrite \Model \ProductUrlRewriteGenerator ;
19+ use Magento \Framework \App \ObjectManager ;
20+ use Magento \Framework \Serialize \Serializer \Json ;
21+ use Magento \UrlRewrite \Model \MergeDataProvider ;
22+ use Magento \UrlRewrite \Model \MergeDataProviderFactory ;
23+ use Magento \UrlRewrite \Model \UrlPersistInterface ;
24+ use Magento \UrlRewrite \Service \V1 \Data \UrlRewrite ;
25+
26+ /**
27+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
28+ */
829class UrlRewriteHandler
930{
1031 /**
11- * @var \Magento\CatalogUrlRewrite\Model\Category\ ChildrenCategoriesProvider
32+ * @var ChildrenCategoriesProvider
1233 */
1334 protected $ childrenCategoriesProvider ;
1435
1536 /**
16- * @var \Magento\CatalogUrlRewrite\Model\ CategoryUrlRewriteGenerator
37+ * @var CategoryUrlRewriteGenerator
1738 */
1839 protected $ categoryUrlRewriteGenerator ;
1940
2041 /**
21- * @var \Magento\CatalogUrlRewrite\Model\ ProductUrlRewriteGenerator
42+ * @var ProductUrlRewriteGenerator
2243 */
2344 protected $ productUrlRewriteGenerator ;
2445
2546 /**
26- * @var \Magento\UrlRewrite\Model\ UrlPersistInterface
47+ * @var UrlPersistInterface
2748 */
2849 protected $ urlPersist ;
2950
@@ -33,44 +54,51 @@ class UrlRewriteHandler
3354 protected $ isSkippedProduct ;
3455
3556 /**
36- * @var \Magento\Catalog\Model\ResourceModel\Product\ CollectionFactory
57+ * @var CollectionFactory
3758 */
3859 protected $ productCollectionFactory ;
3960
4061 /**
41- * @var \Magento\CatalogUrlRewrite\Model\ CategoryProductUrlPathGenerator
62+ * @var CategoryProductUrlPathGenerator
4263 */
4364 private $ categoryBasedProductRewriteGenerator ;
4465
4566 /**
46- * @var \Magento\UrlRewrite\Model\ MergeDataProvider
67+ * @var MergeDataProvider
4768 */
4869 private $ mergeDataProviderPrototype ;
4970
5071 /**
51- * @var \Magento\Framework\Serialize\Serializer\ Json
72+ * @var Json
5273 */
5374 private $ serializer ;
5475
5576 /**
56- * @param \Magento\CatalogUrlRewrite\Model\Category\ChildrenCategoriesProvider $childrenCategoriesProvider
57- * @param \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator
58- * @param \Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator $productUrlRewriteGenerator
59- * @param \Magento\UrlRewrite\Model\UrlPersistInterface $urlPersist
60- * @param \Magento\Catalog\Model\ResourceModel\Product\CollectionFactory $productCollectionFactory
61- * @param \Magento\CatalogUrlRewrite\Model\CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator
62- * @param \Magento\UrlRewrite\Model\MergeDataProviderFactory|null $mergeDataProviderFactory
63- * @param \Magento\Framework\Serialize\Serializer\Json|null $serializer
77+ * @var ProductScopeRewriteGenerator
78+ */
79+ private $ productScopeRewriteGenerator ;
80+
81+ /**
82+ * @param ChildrenCategoriesProvider $childrenCategoriesProvider
83+ * @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator
84+ * @param ProductUrlRewriteGenerator $productUrlRewriteGenerator
85+ * @param UrlPersistInterface $urlPersist
86+ * @param CollectionFactory $productCollectionFactory
87+ * @param CategoryProductUrlPathGenerator $categoryBasedProductRewriteGenerator
88+ * @param MergeDataProviderFactory|null $mergeDataProviderFactory
89+ * @param Json|null $serializer
90+ * @param ProductScopeRewriteGenerator|null $productScopeRewriteGenerator
6491 */
6592 public function __construct (
66- \Magento \CatalogUrlRewrite \Model \Category \ChildrenCategoriesProvider $ childrenCategoriesProvider ,
67- \Magento \CatalogUrlRewrite \Model \CategoryUrlRewriteGenerator $ categoryUrlRewriteGenerator ,
68- \Magento \CatalogUrlRewrite \Model \ProductUrlRewriteGenerator $ productUrlRewriteGenerator ,
69- \Magento \UrlRewrite \Model \UrlPersistInterface $ urlPersist ,
70- \Magento \Catalog \Model \ResourceModel \Product \CollectionFactory $ productCollectionFactory ,
71- \Magento \CatalogUrlRewrite \Model \CategoryProductUrlPathGenerator $ categoryBasedProductRewriteGenerator ,
72- \Magento \UrlRewrite \Model \MergeDataProviderFactory $ mergeDataProviderFactory = null ,
73- \Magento \Framework \Serialize \Serializer \Json $ serializer = null
93+ ChildrenCategoriesProvider $ childrenCategoriesProvider ,
94+ CategoryUrlRewriteGenerator $ categoryUrlRewriteGenerator ,
95+ ProductUrlRewriteGenerator $ productUrlRewriteGenerator ,
96+ UrlPersistInterface $ urlPersist ,
97+ CollectionFactory $ productCollectionFactory ,
98+ CategoryProductUrlPathGenerator $ categoryBasedProductRewriteGenerator ,
99+ MergeDataProviderFactory $ mergeDataProviderFactory = null ,
100+ Json $ serializer = null ,
101+ ProductScopeRewriteGenerator $ productScopeRewriteGenerator = null
74102 ) {
75103 $ this ->childrenCategoriesProvider = $ childrenCategoriesProvider ;
76104 $ this ->categoryUrlRewriteGenerator = $ categoryUrlRewriteGenerator ;
@@ -79,58 +107,29 @@ public function __construct(
79107 $ this ->productCollectionFactory = $ productCollectionFactory ;
80108 $ this ->categoryBasedProductRewriteGenerator = $ categoryBasedProductRewriteGenerator ;
81109
82- if (!isset ($ mergeDataProviderFactory )) {
83- $ mergeDataProviderFactory = \Magento \Framework \App \ObjectManager::getInstance ()->get (
84- \Magento \UrlRewrite \Model \MergeDataProviderFactory::class
85- );
86- }
87-
110+ $ objectManager = ObjectManager::getInstance ();
111+ $ mergeDataProviderFactory = $ mergeDataProviderFactory ?: $ objectManager ->get (MergeDataProviderFactory::class);
88112 $ this ->mergeDataProviderPrototype = $ mergeDataProviderFactory ->create ();
89-
90- $ this ->serializer = $ serializer ?: \Magento \Framework \App \ObjectManager::getInstance ()->get (
91- \Magento \Framework \Serialize \Serializer \Json::class
92- );
113+ $ this ->serializer = $ serializer ?: $ objectManager ->get (Json::class);
114+ $ this ->productScopeRewriteGenerator = $ productScopeRewriteGenerator
115+ ?: $ objectManager ->get (ProductScopeRewriteGenerator::class);
93116 }
94117
95118 /**
96- * Generate url rewrites for products assigned to category
119+ * Generates URL rewrites for products assigned to category.
97120 *
98- * @param \Magento\Catalog\Model\ Category $category
121+ * @param Category $category
99122 * @return array
100123 */
101- public function generateProductUrlRewrites (\ Magento \ Catalog \ Model \ Category $ category )
124+ public function generateProductUrlRewrites (Category $ category ): array
102125 {
103126 $ mergeDataProvider = clone $ this ->mergeDataProviderPrototype ;
104127 $ this ->isSkippedProduct [$ category ->getEntityId ()] = [];
105128 $ saveRewriteHistory = $ category ->getData ('save_rewrites_history ' );
106- $ storeId = $ category ->getStoreId ();
129+ $ storeId = ( int ) $ category ->getStoreId ();
107130
108131 if ($ category ->getChangedProductIds ()) {
109- $ this ->isSkippedProduct [$ category ->getEntityId ()] = $ category ->getAffectedProductIds ();
110- /* @var \Magento\Catalog\Model\ResourceModel\Product\Collection $collection */
111- $ collection = $ this ->productCollectionFactory ->create ()
112- ->setStoreId ($ storeId )
113- ->addIdFilter ($ category ->getAffectedProductIds ())
114- ->addAttributeToSelect ('visibility ' )
115- ->addAttributeToSelect ('name ' )
116- ->addAttributeToSelect ('url_key ' )
117- ->addAttributeToSelect ('url_path ' );
118-
119- $ collection ->setPageSize (1000 );
120- $ pageCount = $ collection ->getLastPageNumber ();
121- $ currentPage = 1 ;
122- while ($ currentPage <= $ pageCount ) {
123- $ collection ->setCurPage ($ currentPage );
124- foreach ($ collection as $ product ) {
125- $ product ->setStoreId ($ storeId );
126- $ product ->setData ('save_rewrites_history ' , $ saveRewriteHistory );
127- $ mergeDataProvider ->merge (
128- $ this ->productUrlRewriteGenerator ->generate ($ product , $ category ->getEntityId ())
129- );
130- }
131- $ collection ->clear ();
132- $ currentPage ++;
133- }
132+ $ this ->generateChangedProductUrls ($ mergeDataProvider , $ category , $ storeId , $ saveRewriteHistory );
134133 } else {
135134 $ mergeDataProvider ->merge (
136135 $ this ->getCategoryProductsUrlRewrites (
@@ -157,21 +156,49 @@ public function generateProductUrlRewrites(\Magento\Catalog\Model\Category $cate
157156 }
158157
159158 /**
160- * @param \Magento\Catalog\Model\Category $category
159+ * @param Category $category
160+ * @return void
161+ */
162+ public function deleteCategoryRewritesForChildren (Category $ category )
163+ {
164+ $ categoryIds = $ this ->childrenCategoriesProvider ->getChildrenIds ($ category , true );
165+ $ categoryIds [] = $ category ->getId ();
166+ foreach ($ categoryIds as $ categoryId ) {
167+ $ this ->urlPersist ->deleteByData (
168+ [
169+ UrlRewrite::ENTITY_ID =>
170+ $ categoryId ,
171+ UrlRewrite::ENTITY_TYPE =>
172+ CategoryUrlRewriteGenerator::ENTITY_TYPE ,
173+ ]
174+ );
175+ $ this ->urlPersist ->deleteByData (
176+ [
177+ UrlRewrite::METADATA =>
178+ $ this ->serializer ->serialize (['category_id ' => $ categoryId ]),
179+ UrlRewrite::ENTITY_TYPE =>
180+ ProductUrlRewriteGenerator::ENTITY_TYPE ,
181+ ]
182+ );
183+ }
184+ }
185+
186+ /**
187+ * @param Category $category
161188 * @param int $storeId
162189 * @param bool $saveRewriteHistory
163190 * @param int|null $rootCategoryId
164191 * @return array
165192 */
166193 private function getCategoryProductsUrlRewrites (
167- \ Magento \ Catalog \ Model \ Category $ category ,
194+ Category $ category ,
168195 $ storeId ,
169196 $ saveRewriteHistory ,
170197 $ rootCategoryId = null
171198 ) {
172199 $ mergeDataProvider = clone $ this ->mergeDataProviderPrototype ;
173200
174- /** @var \Magento\Catalog\Model\ResourceModel\Product\ Collection $productCollection */
201+ /** @var Collection $productCollection */
175202 $ productCollection = $ this ->productCollectionFactory ->create ();
176203
177204 $ productCollection ->addCategoriesFilter (['eq ' => [$ category ->getEntityId ()]])
@@ -199,30 +226,65 @@ private function getCategoryProductsUrlRewrites(
199226 }
200227
201228 /**
202- * @param \Magento\Catalog\Model\Category $category
203- * @return void
229+ * Generates product URL rewrites.
230+ *
231+ * @param MergeDataProvider $mergeDataProvider
232+ * @param Category $category
233+ * @param Product $product
234+ * @param int $storeId
235+ * @param $saveRewriteHistory
204236 */
205- public function deleteCategoryRewritesForChildren (\Magento \Catalog \Model \Category $ category )
206- {
207- $ categoryIds = $ this ->childrenCategoriesProvider ->getChildrenIds ($ category , true );
208- $ categoryIds [] = $ category ->getId ();
209- foreach ($ categoryIds as $ categoryId ) {
210- $ this ->urlPersist ->deleteByData (
211- [
212- \Magento \UrlRewrite \Service \V1 \Data \UrlRewrite::ENTITY_ID =>
213- $ categoryId ,
214- \Magento \UrlRewrite \Service \V1 \Data \UrlRewrite::ENTITY_TYPE =>
215- \Magento \CatalogUrlRewrite \Model \CategoryUrlRewriteGenerator::ENTITY_TYPE ,
216- ]
217- );
218- $ this ->urlPersist ->deleteByData (
219- [
220- \Magento \UrlRewrite \Service \V1 \Data \UrlRewrite::METADATA =>
221- $ this ->serializer ->serialize (['category_id ' => $ categoryId ]),
222- \Magento \UrlRewrite \Service \V1 \Data \UrlRewrite::ENTITY_TYPE =>
223- \Magento \CatalogUrlRewrite \Model \ProductUrlRewriteGenerator::ENTITY_TYPE ,
224- ]
225- );
237+ private function generateChangedProductUrls (
238+ MergeDataProvider $ mergeDataProvider ,
239+ Category $ category ,
240+ int $ storeId ,
241+ $ saveRewriteHistory
242+ ) {
243+ $ this ->isSkippedProduct [$ category ->getEntityId ()] = $ category ->getAffectedProductIds ();
244+
245+ $ categoryStoreIds = [$ storeId ];
246+ // If category is changed in the Global scope when need to regenerate product URL rewrites for all other scopes.
247+ if ($ this ->productScopeRewriteGenerator ->isGlobalScope ($ storeId )) {
248+ $ categoryStoreIds = $ this ->getCategoryStoreIds ($ category );
249+ }
250+
251+ foreach ($ categoryStoreIds as $ categoryStoreId ) {
252+ /* @var Collection $collection */
253+ $ collection = $ this ->productCollectionFactory ->create ()
254+ ->setStoreId ($ categoryStoreId )
255+ ->addIdFilter ($ category ->getAffectedProductIds ())
256+ ->addAttributeToSelect ('visibility ' )
257+ ->addAttributeToSelect ('name ' )
258+ ->addAttributeToSelect ('url_key ' )
259+ ->addAttributeToSelect ('url_path ' );
260+
261+ $ collection ->setPageSize (1000 );
262+ $ pageCount = $ collection ->getLastPageNumber ();
263+ $ currentPage = 1 ;
264+ while ($ currentPage <= $ pageCount ) {
265+ $ collection ->setCurPage ($ currentPage );
266+ foreach ($ collection as $ product ) {
267+ $ product ->setData ('save_rewrites_history ' , $ saveRewriteHistory );
268+ $ product ->setStoreId ($ categoryStoreId );
269+ $ mergeDataProvider ->merge (
270+ $ this ->productUrlRewriteGenerator ->generate ($ product , $ category ->getEntityId ())
271+ );
272+ }
273+ $ collection ->clear ();
274+ $ currentPage ++;
275+ }
226276 }
227277 }
278+
279+ /**
280+ * Gets category store IDs without Global Store.
281+ *
282+ * @param Category $category
283+ * @return array
284+ */
285+ private function getCategoryStoreIds (Category $ category ): array
286+ {
287+ $ ids = $ category ->getStoreIds ();
288+ return array_filter ($ ids );
289+ }
228290}
0 commit comments