diff --git a/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php b/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php index 9d26184e2c2d4..7bf1da2b814e3 100644 --- a/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php +++ b/app/code/Magento/CatalogUrlRewrite/Model/ProductScopeRewriteGenerator.php @@ -6,6 +6,7 @@ namespace Magento\CatalogUrlRewrite\Model; use Magento\Catalog\Api\CategoryRepositoryInterface; +use Magento\Catalog\Api\Data\CategoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Product; use Magento\CatalogUrlRewrite\Model\Product\AnchorUrlRewriteGenerator; @@ -15,12 +16,14 @@ use Magento\CatalogUrlRewrite\Service\V1\StoreViewService; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\ObjectManager; +use Magento\Framework\Exception\NoSuchEntityException; use Magento\Store\Model\Store; use Magento\Store\Model\StoreManagerInterface; use Magento\UrlRewrite\Model\MergeDataProviderFactory; /** - * Class ProductScopeRewriteGenerator + * Generates Product/Category URLs for different scopes + * * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ProductScopeRewriteGenerator @@ -174,7 +177,6 @@ public function generateForSpecificStoreView($storeId, $productCategories, Produ continue; } - // category should be loaded per appropriate store if category's URL key has been changed $categories[] = $this->getCategoryWithOverriddenUrlKey($storeId, $category); } @@ -240,9 +242,15 @@ public function isCategoryProperForGenerating(Category $category, $storeId) * Checks if URL key has been changed for provided category and returns reloaded category, * in other case - returns provided category. * + * Category should be loaded per appropriate store at all times. This is because whilst the URL key on the + * category in focus might be unchanged, parent category URL keys might be. If the category store ID + * and passed store ID are the same then return current category as it is correct but may have changed in memory + * * @param int $storeId * @param Category $category - * @return Category + * + * @return CategoryInterface + * @throws NoSuchEntityException */ private function getCategoryWithOverriddenUrlKey($storeId, Category $category) { @@ -252,9 +260,10 @@ private function getCategoryWithOverriddenUrlKey($storeId, Category $category) Category::ENTITY ); - if (!$isUrlKeyOverridden) { + if (!$isUrlKeyOverridden && $storeId === $category->getStoreId()) { return $category; } + return $this->categoryRepository->get($category->getEntityId(), $storeId); } diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryUrlRewriteDifferentStoreTest.xml b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryUrlRewriteDifferentStoreTest.xml new file mode 100644 index 0000000000000..776b5b9b70f33 --- /dev/null +++ b/app/code/Magento/CatalogUrlRewrite/Test/Mftf/Test/StorefrontCategoryUrlRewriteDifferentStoreTest.xml @@ -0,0 +1,80 @@ + + + + + + + + <description value="Verify url category for different store view, after change ukr_key category for one of them store view."/> + <features value="CatalogUrlRewrite"/> + <severity value="AVERAGE"/> + <testCaseId value="MC-38053"/> + </annotations> + <before> + <magentoCLI command="config:set catalog/seo/product_use_categories 1" stepKey="setEnableUseCategoriesPath"/> + <createData entity="SubCategory" stepKey="rootCategory"/> + <createData entity="SimpleSubCategoryDifferentUrlStore" stepKey="subCategory"> + <requiredEntity createDataKey="rootCategory"/> + </createData> + <createData entity="_defaultProduct" stepKey="createProduct"> + <requiredEntity createDataKey="subCategory"/> + </createData> + <actionGroup ref="AdminLoginActionGroup" stepKey="loginAsAdmin"/> + + <actionGroup ref="CreateStoreViewActionGroup" stepKey="createCustomStoreViewFr"> + <argument name="storeView" value="customStoreFR"/> + </actionGroup> + + <actionGroup ref="CliIndexerReindexActionGroup" stepKey="indexerReindexAfterCreate"> + <argument name="indices" value=""/> + </actionGroup> + <actionGroup ref="CliCacheFlushActionGroup" stepKey="flushCacheBefore"> + <argument name="tags" value=""/> + </actionGroup> + </before> + <after> + <magentoCLI command="config:set catalog/seo/product_use_categories 0" stepKey="setEnableUseCategoriesPath"/> + <deleteData stepKey="deleteProduct" createDataKey="createProduct"/> + <deleteData stepKey="deleteSubCategory" createDataKey="subCategory"/> + <deleteData stepKey="deleteRootCategory" createDataKey="rootCategory"/> + + <actionGroup ref="AdminDeleteStoreViewActionGroup" stepKey="deleteStoreView"> + <argument name="customStore" value="customStoreFR"/> + </actionGroup> + <actionGroup ref="AdminLogoutActionGroup" stepKey="logout"/> + </after> + + <actionGroup ref="NavigateToCreatedCategoryActionGroup" stepKey="navigateToCreatedSubCategory"> + <argument name="Category" value="$$subCategory$$"/> + </actionGroup> + <actionGroup ref="AdminSwitchStoreViewActionGroup" stepKey="AdminSwitchCustomStoreViewForSubCategory"> + <argument name="storeView" value="customStoreFR.name"/> + </actionGroup> + <actionGroup ref="ChangeSeoUrlKeyForSubCategoryActionGroup" stepKey="changeSeoUrlKeyForSubCategoryCustomStore"> + <argument name="value" value="{{SimpleSubCategoryDifferentUrlStore.url_key_custom_store}}"/> + </actionGroup> + + <actionGroup ref="StorefrontGoToSubCategoryPageActionGroup" stepKey="goToCategoryC"> + <argument name="categoryName" value="$$rootCategory.name$$"/> + <argument name="subCategoryName" value="$$subCategory.name$$"/> + </actionGroup> + + <click selector="{{StorefrontCategoryProductSection.ProductInfoByName($$createProduct.name$$)}}" stepKey="navigateToCreateProduct"/> + + <actionGroup ref="StorefrontSwitchStoreViewActionGroup" stepKey="switchStore"> + <argument name="storeView" value="customStoreFR" /> + </actionGroup> + + <grabFromCurrentUrl stepKey="grabUrl"/> + <assertStringContainsString stepKey="assertUrl"> + <expectedResult type="string">{{SimpleSubCategoryDifferentUrlStore.url_key_custom_store}}</expectedResult> + <actualResult type="string">{$grabUrl}</actualResult> + </assertStringContainsString> + </test> +</tests> diff --git a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php index 7b18461a580fe..d9c6adce9661f 100644 --- a/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php +++ b/app/code/Magento/CatalogUrlRewrite/Test/Unit/Model/ProductScopeRewriteGeneratorTest.php @@ -7,6 +7,7 @@ namespace Magento\CatalogUrlRewrite\Test\Unit\Model; +use Magento\Catalog\Api\CategoryRepositoryInterface; use Magento\Catalog\Model\Category; use Magento\Catalog\Model\Product; use Magento\CatalogUrlRewrite\Model\ObjectRegistry; @@ -69,6 +70,9 @@ class ProductScopeRewriteGeneratorTest extends TestCase /** @var ScopeConfigInterface|MockObject */ private $configMock; + /** @var CategoryRepositoryInterface|MockObject */ + private $categoryRepositoryMock; + protected function setUp(): void { $this->serializer = $this->createMock(Json::class); @@ -126,6 +130,8 @@ function ($value) { $this->configMock = $this->getMockBuilder(ScopeConfigInterface::class) ->getMock(); + $this->categoryRepositoryMock = $this->getMockForAbstractClass(CategoryRepositoryInterface::class); + $this->productScopeGenerator = (new ObjectManager($this))->getObject( ProductScopeRewriteGenerator::class, [ @@ -137,7 +143,8 @@ function ($value) { 'storeViewService' => $this->storeViewService, 'storeManager' => $this->storeManager, 'mergeDataProviderFactory' => $mergeDataProviderFactory, - 'config' => $this->configMock + 'config' => $this->configMock, + 'categoryRepository' => $this->categoryRepositoryMock ] ); $this->categoryMock = $this->getMockBuilder(Category::class) @@ -215,6 +222,8 @@ public function testGenerationForSpecificStore() $this->anchorUrlRewriteGenerator->expects($this->any())->method('generate') ->willReturn([]); + $this->categoryRepositoryMock->expects($this->once())->method('get')->willReturn($this->categoryMock); + $this->assertEquals( ['category-1_1' => $canonical], $this->productScopeGenerator->generateForSpecificStoreView(1, [$this->categoryMock], $product, 1)