Skip to content

Commit 8d79a4c

Browse files
committed
Merge branch 'ACP2E-1754' of https://github.com/magento-l3/magento2ce into L3-PR-2023-06-23
2 parents eb6eed2 + 01a3d98 commit 8d79a4c

File tree

4 files changed

+267
-23
lines changed

4 files changed

+267
-23
lines changed

app/code/Magento/CatalogUrlRewrite/Observer/AfterImportDataObserver.php

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
namespace Magento\CatalogUrlRewrite\Observer;
88

9+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
10+
use Magento\Catalog\Api\Data\ProductInterface;
911
use Magento\Catalog\Model\Category;
1012
use Magento\Catalog\Model\Product;
1113
use Magento\Catalog\Model\Product\Visibility;
@@ -19,6 +21,7 @@
1921
use Magento\CatalogUrlRewrite\Model\ProductUrlPathGenerator;
2022
use Magento\CatalogUrlRewrite\Model\ProductUrlRewriteGenerator;
2123
use Magento\CatalogUrlRewrite\Service\V1\StoreViewService;
24+
use Magento\Eav\Model\ResourceModel\AttributeValue;
2225
use Magento\Framework\App\Config\ScopeConfigInterface;
2326
use Magento\Framework\App\ObjectManager;
2427
use Magento\Framework\DataObject;
@@ -40,6 +43,7 @@
4043
/**
4144
* @SuppressWarnings(PHPMD.TooManyFields)
4245
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
46+
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
4347
*/
4448
class AfterImportDataObserver implements ObserverInterface
4549
{
@@ -187,6 +191,16 @@ class AfterImportDataObserver implements ObserverInterface
187191
*/
188192
private $productCollectionFactory;
189193

194+
/**
195+
* @var AttributeValue
196+
*/
197+
private $attributeValue;
198+
199+
/**
200+
* @var null|array
201+
*/
202+
private $cachedValues = null;
203+
190204
/**
191205
* @param ProductFactory $catalogProductFactory
192206
* @param ObjectRegistryFactory $objectRegistryFactory
@@ -200,6 +214,7 @@ class AfterImportDataObserver implements ObserverInterface
200214
* @param CategoryCollectionFactory|null $categoryCollectionFactory
201215
* @param ScopeConfigInterface|null $scopeConfig
202216
* @param CollectionFactory|null $collectionFactory
217+
* @param AttributeValue|null $attributeValue
203218
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
204219
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
205220
*/
@@ -215,7 +230,8 @@ public function __construct(
215230
MergeDataProviderFactory $mergeDataProviderFactory = null,
216231
CategoryCollectionFactory $categoryCollectionFactory = null,
217232
ScopeConfigInterface $scopeConfig = null,
218-
CollectionFactory $collectionFactory = null
233+
CollectionFactory $collectionFactory = null,
234+
AttributeValue $attributeValue = null
219235
) {
220236
$this->urlPersist = $urlPersist;
221237
$this->catalogProductFactory = $catalogProductFactory;
@@ -234,6 +250,8 @@ public function __construct(
234250
ObjectManager::getInstance()->get(ScopeConfigInterface::class);
235251
$this->productCollectionFactory = $collectionFactory ?:
236252
ObjectManager::getInstance()->get(CollectionFactory::class);
253+
$this->attributeValue = $attributeValue ?:
254+
ObjectManager::getInstance()->get(AttributeValue::class);
237255
}
238256

239257
/**
@@ -343,7 +361,7 @@ private function isNeedToPopulateForUrlGeneration($rowData, $newSku, $oldSku): b
343361
|| (array_key_exists(strtolower($rowData[ImportProduct::COL_SKU] ?? ''), $oldSku)
344362
&& !isset($rowData[self::URL_KEY_ATTRIBUTE_CODE])
345363
&& $this->import->getBehavior() === ImportExport::BEHAVIOR_APPEND)
346-
)
364+
)
347365
&& !isset($rowData["categories"])
348366
) {
349367
return false;
@@ -446,18 +464,90 @@ private function canonicalUrlRewriteGenerate(array $products)
446464
foreach ($products as $productId => $productsByStores) {
447465
foreach ($productsByStores as $storeId => $product) {
448466
if ($this->productUrlPathGenerator->getUrlPath($product)) {
467+
$reqPath = $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId);
468+
$targetPath = $this->productUrlPathGenerator->getCanonicalUrlPath($product);
469+
if ((int) $storeId !== (int) $product->getStoreId()
470+
&& $this->isGlobalScope($product->getStoreId())) {
471+
$this->initializeCacheForProducts($products);
472+
$reqPath = $this->getReqPath((int)$productId, (int)$storeId, $product);
473+
}
449474
$urls[] = $this->urlRewriteFactory->create()
450475
->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE)
451476
->setEntityId($productId)
452-
->setRequestPath($this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId))
453-
->setTargetPath($this->productUrlPathGenerator->getCanonicalUrlPath($product))
477+
->setRequestPath($reqPath)
478+
->setTargetPath($targetPath)
454479
->setStoreId($storeId);
455480
}
456481
}
457482
}
458483
return $urls;
459484
}
460485

486+
/**
487+
* Initialization for cache with scop based values
488+
*
489+
* @param array $products
490+
* @return void
491+
*/
492+
private function initializeCacheForProducts(array $products) : void
493+
{
494+
if ($this->cachedValues === null) {
495+
$this->cachedValues = $this->getScopeBasedUrlKeyValues($products);
496+
}
497+
}
498+
499+
/**
500+
* Get request path for the selected scope
501+
*
502+
* @param int $productId
503+
* @param int $storeId
504+
* @param Product $product
505+
* @param Category|null $category
506+
* @return string
507+
*/
508+
private function getReqPath(int $productId, int $storeId, Product $product, ?Category $category = null) : string
509+
{
510+
$reqPath = $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId, $category);
511+
if (!empty($this->cachedValues) && isset($this->cachedValues[$productId][$storeId])) {
512+
$storeProduct = clone $product;
513+
$storeProduct->setStoreId($storeId);
514+
$storeProduct->setUrlKey($this->cachedValues[$productId][$storeId]);
515+
$reqPath = $this->productUrlPathGenerator->getUrlPathWithSuffix($storeProduct, $storeId, $category);
516+
}
517+
return $reqPath;
518+
}
519+
520+
/**
521+
* Get url key attribute values for the specified scope
522+
*
523+
* @param array $products
524+
* @return array
525+
*/
526+
private function getScopeBasedUrlKeyValues(array $products) : array
527+
{
528+
$values = [];
529+
$productIds = [];
530+
$storeIds = [];
531+
foreach ($products as $productId => $productsByStores) {
532+
$productIds[] = (int) $productId;
533+
foreach (array_keys($productsByStores) as $id) {
534+
$storeIds[] = (int) $id;
535+
}
536+
}
537+
$productIds = array_unique($productIds);
538+
$storeIds = array_unique($storeIds);
539+
if (!empty($productIds) && !empty($storeIds)) {
540+
$values = $this->attributeValue->getValuesMultiple(
541+
ProductInterface::class,
542+
$productIds,
543+
[ProductAttributeInterface::CODE_SEO_FIELD_URL_KEY],
544+
$storeIds
545+
);
546+
}
547+
548+
return $values;
549+
}
550+
461551
/**
462552
* Generate list based on categories.
463553
*
@@ -476,12 +566,18 @@ private function categoriesUrlRewriteGenerate(array $products): array
476566
continue;
477567
}
478568
$requestPath = $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId, $category);
569+
$targetPath = $this->productUrlPathGenerator->getCanonicalUrlPath($product, $category);
570+
if ((int) $storeId !== (int) $product->getStoreId()
571+
&& $this->isGlobalScope($product->getStoreId())) {
572+
$this->initializeCacheForProducts($products);
573+
$requestPath = $this->getReqPath((int)$productId, (int)$storeId, $product, $category);
574+
}
479575
$urls[] = [
480576
$this->urlRewriteFactory->create()
481577
->setEntityType(ProductUrlRewriteGenerator::ENTITY_TYPE)
482578
->setEntityId($productId)
483579
->setRequestPath($requestPath)
484-
->setTargetPath($this->productUrlPathGenerator->getCanonicalUrlPath($product, $category))
580+
->setTargetPath($targetPath)
485581
->setStoreId($storeId)
486582
->setMetadata(['category_id' => $category->getId()])
487583
];
@@ -570,6 +666,7 @@ private function generateForAutogenerated(UrlRewrite $url, ?Category $category,
570666
* @param Category|null $category
571667
* @param Product[] $products
572668
* @return UrlRewrite[]
669+
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
573670
*/
574671
private function generateForCustom(UrlRewrite $url, ?Category $category, array $products) : array
575672
{
@@ -580,6 +677,18 @@ private function generateForCustom(UrlRewrite $url, ?Category $category, array $
580677
$targetPath = $url->getRedirectType()
581678
? $this->productUrlPathGenerator->getUrlPathWithSuffix($product, $storeId, $category)
582679
: $url->getTargetPath();
680+
if ((int) $storeId !== (int) $product->getStoreId()
681+
&& $this->isGlobalScope($product->getStoreId())) {
682+
$this->initializeCacheForProducts($products);
683+
if (!empty($this->cachedValues) && isset($this->cachedValues[$productId][$storeId])) {
684+
$storeProduct = clone $product;
685+
$storeProduct->setStoreId($storeId);
686+
$storeProduct->setUrlKey($this->cachedValues[$productId][$storeId]);
687+
$targetPath = $url->getRedirectType()
688+
? $this->productUrlPathGenerator->getUrlPathWithSuffix($storeProduct, $storeId, $category)
689+
: $url->getTargetPath();
690+
}
691+
}
583692
if ($url->getRequestPath() === $targetPath) {
584693
return [];
585694
}

app/code/Magento/CatalogUrlRewrite/Test/Unit/Observer/AfterImportDataObserverTest.php

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@
77

88
namespace Magento\CatalogUrlRewrite\Test\Unit\Observer;
99

10+
use Magento\Catalog\Api\Data\ProductAttributeInterface;
11+
use Magento\Catalog\Api\Data\ProductInterface;
12+
use Magento\Eav\Model\ResourceModel\AttributeValue;
1013
use Magento\Catalog\Api\ProductRepositoryInterface;
1114
use Magento\Catalog\Model\ProductFactory;
1215
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
16+
use Magento\Catalog\Model\ResourceModel\Product\CollectionFactory;
1317
use Magento\CatalogImportExport\Model\Import\Product as ImportProduct;
1418
use Magento\CatalogUrlRewrite\Model\ObjectRegistry;
1519
use Magento\CatalogUrlRewrite\Model\ObjectRegistryFactory;
@@ -18,6 +22,7 @@
1822
use Magento\CatalogUrlRewrite\Observer\AfterImportDataObserver;
1923
use Magento\CatalogUrlRewrite\Service\V1\StoreViewService;
2024
use Magento\Framework\Event;
25+
use Magento\Framework\App\Config\ScopeConfigInterface;
2126
use Magento\Framework\Event\Observer;
2227
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
2328
use Magento\Store\Model\Store;
@@ -133,6 +138,21 @@ class AfterImportDataObserverTest extends TestCase
133138
*/
134139
private $categoryCollectionFactory;
135140

141+
/**
142+
* @var AttributeValue|MockObject
143+
*/
144+
private $attributeValue;
145+
146+
/**
147+
* @var ScopeConfigInterface|MockObject
148+
*/
149+
private $scopeConfig;
150+
151+
/**
152+
* @var CollectionFactory|MockObject
153+
*/
154+
private $collectionFactory;
155+
136156
/**
137157
* Test products returned by getBunch method of event object.
138158
*
@@ -252,21 +272,29 @@ protected function setUp(): void
252272
->setMethods(['create'])
253273
->disableOriginalConstructor()
254274
->getMock();
255-
$this->objectManager = new ObjectManager($this);
256-
$this->import = $this->objectManager->getObject(
257-
AfterImportDataObserver::class,
258-
[
259-
'catalogProductFactory' => $this->catalogProductFactory,
260-
'objectRegistryFactory' => $this->objectRegistryFactory,
261-
'productUrlPathGenerator' => $this->productUrlPathGenerator,
262-
'storeViewService' => $this->storeViewService,
263-
'storeManager'=> $this->storeManager,
264-
'urlPersist' => $this->urlPersist,
265-
'urlRewriteFactory' => $this->urlRewriteFactory,
266-
'urlFinder' => $this->urlFinder,
267-
'mergeDataProviderFactory' => $mergeDataProviderFactory,
268-
'categoryCollectionFactory' => $this->categoryCollectionFactory
269-
]
275+
$this->attributeValue = $this->getMockBuilder(AttributeValue::class)
276+
->disableOriginalConstructor()
277+
->getMock();
278+
$this->scopeConfig = $this->getMockBuilder(ScopeConfigInterface::class)
279+
->disableOriginalConstructor()
280+
->getMockForAbstractClass();
281+
$this->collectionFactory = $this->getMockBuilder(CollectionFactory::class)
282+
->disableOriginalConstructor()
283+
->getMock();
284+
$this->import = new AfterImportDataObserver(
285+
$this->catalogProductFactory,
286+
$this->objectRegistryFactory,
287+
$this->productUrlPathGenerator,
288+
$this->storeViewService,
289+
$this->storeManager,
290+
$this->urlPersist,
291+
$this->urlRewriteFactory,
292+
$this->urlFinder,
293+
$mergeDataProviderFactory,
294+
$this->categoryCollectionFactory,
295+
$this->scopeConfig,
296+
$this->collectionFactory,
297+
$this->attributeValue
270298
);
271299
}
272300

@@ -389,6 +417,13 @@ public function testAfterImportData()
389417
->expects($this->once())
390418
->method('replace')
391419
->with($productUrls);
420+
$this->attributeValue->expects($this->once())
421+
->method('getValuesMultiple')
422+
->with(ProductInterface::class, [0], [ProductAttributeInterface::CODE_SEO_FIELD_URL_KEY], [1]);
423+
$this->scopeConfig->expects($this->once())
424+
->method('getValue')
425+
->with('catalog/seo/generate_category_product_rewrites')
426+
->willReturn(true);
392427
$this->import->execute($this->observer);
393428
}
394429

0 commit comments

Comments
 (0)