Skip to content

Commit 8da57e4

Browse files
ENGCOM-8274: BUG - Prevent Magento from Creating incorrect URLs for category/products when Store View has modified URL keys #28676
2 parents c4fbc77 + abddda7 commit 8da57e4

File tree

3 files changed

+103
-5
lines changed

3 files changed

+103
-5
lines changed

app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\CatalogUrlRewrite\Model;
77

88
use Magento\Catalog\Api\CategoryRepositoryInterface;
9+
use Magento\Catalog\Api\Data\CategoryInterface;
910
use Magento\Catalog\Model\Category;
1011
use Magento\Catalog\Model\Product;
1112
use Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator;
@@ -15,12 +16,14 @@
1516
use Magento\CatalogUrlRewrite\Service\V1\StoreViewService;
1617
use Magento\Framework\App\Config\ScopeConfigInterface;
1718
use Magento\Framework\App\ObjectManager;
19+
use Magento\Framework\Exception\NoSuchEntityException;
1820
use Magento\Store\Model\Store;
1921
use Magento\Store\Model\StoreManagerInterface;
2022
use Magento\UrlRewrite\Model\MergeDataProviderFactory;
2123

2224
/**
23-
* Class ProductScopeRewriteGenerator
25+
* Generates Product/Category URLs for different scopes
26+
*
2427
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2528
*/
2629
class ProductScopeRewriteGenerator
@@ -174,7 +177,6 @@ public function generateForSpecificStoreView($storeId, $productCategories, Produ
174177
continue;
175178
}
176179

177-
// category should be loaded per appropriate store if category's URL key has been changed
178180
$categories[] = $this->getCategoryWithOverriddenUrlKey($storeId, $category);
179181
}
180182

@@ -240,9 +242,15 @@ public function isCategoryProperForGenerating(Category $category, $storeId)
240242
* Checks if URL key has been changed for provided category and returns reloaded category,
241243
* in other case - returns provided category.
242244
*
245+
* Category should be loaded per appropriate store at all times. This is because whilst the URL key on the
246+
* category in focus might be unchanged, parent category URL keys might be. If the category store ID
247+
* and passed store ID are the same then return current category as it is correct but may have changed in memory
248+
*
243249
* @param int $storeId
244250
* @param Category $category
245-
* @return Category
251+
*
252+
* @return CategoryInterface
253+
* @throws NoSuchEntityException
246254
*/
247255
private function getCategoryWithOverriddenUrlKey($storeId, Category $category)
248256
{
@@ -252,9 +260,10 @@ private function getCategoryWithOverriddenUrlKey($storeId, Category $category)
252260
Category::ENTITY
253261
);
254262

255-
if (!$isUrlKeyOverridden) {
263+
if (!$isUrlKeyOverridden && $storeId === $category->getStoreId()) {
256264
return $category;
257265
}
266+
258267
return $this->categoryRepository->get($category->getEntityId(), $storeId);
259268
}
260269

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="StorefrontCategoryUrlRewriteDifferentStoreTest">
11+
<annotations>
12+
<stories value="Url rewrites"/>
13+
<title value="Verify url category for different store view."/>
14+
<description value="Verify url category for different store view, after change ukr_key category for one of them store view."/>
15+
<features value="CatalogUrlRewrite"/>
16+
<severity value="AVERAGE"/>
17+
<testCaseId value="MC-38053"/>
18+
</annotations>
19+
<before>
20+
<magentoCLI command="config:set catalog/seo/product_use_categories 1" stepKey="setEnableUseCategoriesPath"/>
21+
<createData entity="SubCategory" stepKey="rootCategory"/>
22+
<createData entity="SimpleSubCategoryDifferentUrlStore" stepKey="subCategory">
23+
<requiredEntity createDataKey="rootCategory"/>
24+
</createData>
25+
<createData entity="_defaultProduct" stepKey="createProduct">
26+
<requiredEntity createDataKey="subCategory"/>
27+
</createData>
28+
<actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/>
29+
30+
<actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr">
31+
<argument name="storeView" value="customStoreFR"/>
32+
</actionGroup>
33+
34+
<actionGroup ref="CliIndexerReindexActionGroup" stepKey="indexerReindexAfterCreate">
35+
<argument name="indices" value=""/>
36+
</actionGroup>
37+
<actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCacheBefore">
38+
<argument name="tags" value=""/>
39+
</actionGroup>
40+
</before>
41+
<after>
42+
<magentoCLI command="config:set catalog/seo/product_use_categories 0" stepKey="setEnableUseCategoriesPath"/>
43+
<deleteData stepKey="deleteProduct" createDataKey="createProduct"/>
44+
<deleteData stepKey="deleteSubCategory" createDataKey="subCategory"/>
45+
<deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/>
46+
47+
<actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView">
48+
<argument name="customStore" value="customStoreFR"/>
49+
</actionGroup>
50+
<actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/>
51+
</after>
52+
53+
<actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="navigateToCreatedSubCategory">
54+
<argument name="Category" value="$$subCategory$$"/>
55+
</actionGroup>
56+
<actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchCustomStoreViewForSubCategory">
57+
<argument name="storeView" value="customStoreFR.name"/>
58+
</actionGroup>
59+
<actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyForSubCategoryCustomStore">
60+
<argument name="value" value="{{SimpleSubCategoryDifferentUrlStore.url_key_custom_store}}"/>
61+
</actionGroup>
62+
63+
<actionGroup ref="StorefrontGoToSubCategoryPageActionGroup" stepKey="goToCategoryC">
64+
<argument name="categoryName" value="$$rootCategory.name$$"/>
65+
<argument name="subCategoryName" value="$$subCategory.name$$"/>
66+
</actionGroup>
67+
68+
<click selector="{{StorefrontCategoryProductSection.ProductInfoByName($$createProduct.name$$)}}" stepKey="navigateToCreateProduct"/>
69+
70+
<actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStore">
71+
<argument name="storeView" value="customStoreFR" />
72+
</actionGroup>
73+
74+
<grabFromCurrentUrl stepKey="grabUrl"/>
75+
<assertStringContainsString stepKey="assertUrl">
76+
<expectedResult type="string">{{SimpleSubCategoryDifferentUrlStore.url_key_custom_store}}</expectedResult>
77+
<actualResult type="string">{$grabUrl}</actualResult>
78+
</assertStringContainsString>
79+
</test>
80+
</tests>

app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

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

10+
use Magento\Catalog\Api\CategoryRepositoryInterface;
1011
use Magento\Catalog\Model\Category;
1112
use Magento\Catalog\Model\Product;
1213
use Magento\CatalogUrlRewrite\Model\ObjectRegistry;
@@ -69,6 +70,9 @@ class ProductScopeRewriteGeneratorTest extends TestCase
6970
/** @var ScopeConfigInterface|MockObject */
7071
private $configMock;
7172

73+
/** @var CategoryRepositoryInterface|MockObject */
74+
private $categoryRepositoryMock;
75+
7276
protected function setUp(): void
7377
{
7478
$this->serializer = $this->createMock(Json::class);
@@ -126,6 +130,8 @@ function ($value) {
126130
$this->configMock = $this->getMockBuilder(ScopeConfigInterface::class)
127131
->getMock();
128132

133+
$this->categoryRepositoryMock = $this->getMockForAbstractClass(CategoryRepositoryInterface::class);
134+
129135
$this->productScopeGenerator = (new ObjectManager($this))->getObject(
130136
ProductScopeRewriteGenerator::class,
131137
[
@@ -137,7 +143,8 @@ function ($value) {
137143
'storeViewService' => $this->storeViewService,
138144
'storeManager' => $this->storeManager,
139145
'mergeDataProviderFactory' => $mergeDataProviderFactory,
140-
'config' => $this->configMock
146+
'config' => $this->configMock,
147+
'categoryRepository' => $this->categoryRepositoryMock
141148
]
142149
);
143150
$this->categoryMock = $this->getMockBuilder(Category::class)
@@ -215,6 +222,8 @@ public function testGenerationForSpecificStore()
215222
$this->anchorUrlRewriteGenerator->expects($this->any())->method('generate')
216223
->willReturn([]);
217224

225+
$this->categoryRepositoryMock->expects($this->once())->method('get')->willReturn($this->categoryMock);
226+
218227
$this->assertEquals(
219228
['category-1_1' => $canonical],
220229
$this->productScopeGenerator->generateForSpecificStoreView(1, [$this->categoryMock], $product, 1)

0 commit comments

Comments
 (0)