Skip to content

Commit 6b42784

Browse files
committed
Merge remote-tracking branch 'origin/MAGETWO-87526' into PANDA-FIXES-2.2
2 parents 4d42d64 + 065a9b7 commit 6b42784

File tree

4 files changed

+317
-0
lines changed

4 files changed

+317
-0
lines changed
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\CatalogUrlRewrite\Model\Category\Plugin\Category;
7+
8+
use Magento\Framework\Model\AbstractModel;
9+
use Magento\Catalog\Model\Category;
10+
use Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator;
11+
use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator;
12+
use Magento\CatalogUrlRewrite\Service\V1\StoreViewService;
13+
use Magento\UrlRewrite\Model\UrlPersistInterface;
14+
use Magento\Store\Model\Store;
15+
use Magento\Catalog\Model\ResourceModel\Category as CategoryResource;
16+
17+
/**
18+
* Generate and save url-rewrites for category if its parent have specified url-key for different store views
19+
*/
20+
class UpdateUrlPath
21+
{
22+
/**
23+
* @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator
24+
*/
25+
private $categoryUrlPathGenerator;
26+
27+
/**
28+
* @var \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator
29+
*/
30+
private $categoryUrlRewriteGenerator;
31+
32+
/**
33+
* @var \Magento\CatalogUrlRewrite\Service\V1\StoreViewService
34+
*/
35+
private $storeViewService;
36+
37+
/**
38+
* @var \Magento\UrlRewrite\Model\UrlPersistInterface
39+
*/
40+
private $urlPersist;
41+
42+
/**
43+
* @param CategoryUrlPathGenerator $categoryUrlPathGenerator
44+
* @param CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator
45+
* @param UrlPersistInterface $urlPersist
46+
* @param StoreViewService $storeViewService
47+
*/
48+
public function __construct(
49+
CategoryUrlPathGenerator $categoryUrlPathGenerator,
50+
CategoryUrlRewriteGenerator $categoryUrlRewriteGenerator,
51+
UrlPersistInterface $urlPersist,
52+
StoreViewService $storeViewService
53+
) {
54+
$this->categoryUrlPathGenerator = $categoryUrlPathGenerator;
55+
$this->categoryUrlRewriteGenerator = $categoryUrlRewriteGenerator;
56+
$this->urlPersist = $urlPersist;
57+
$this->storeViewService = $storeViewService;
58+
}
59+
60+
/**
61+
* Perform url updating for different stores
62+
*
63+
* @param CategoryResource $subject
64+
* @param CategoryResource $result
65+
* @param AbstractModel $category
66+
* @return CategoryResource
67+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
68+
*/
69+
public function afterSave(
70+
CategoryResource $subject,
71+
CategoryResource $result,
72+
AbstractModel $category
73+
) {
74+
$parentCategoryId = $category->getParentId();
75+
if ($category->isObjectNew()
76+
&& !$category->isInRootCategoryList()
77+
&& !empty($parentCategoryId)) {
78+
foreach ($category->getStoreIds() as $storeId) {
79+
if (!$this->isGlobalScope($storeId)
80+
&& $this->storeViewService->doesEntityHaveOverriddenUrlPathForStore(
81+
$storeId,
82+
$parentCategoryId,
83+
Category::ENTITY
84+
)
85+
) {
86+
$category->setStoreId($storeId);
87+
$this->updateUrlPathForCategory($category, $subject);
88+
$this->urlPersist->replace($this->categoryUrlRewriteGenerator->generate($category));
89+
}
90+
}
91+
}
92+
return $result;
93+
}
94+
95+
/**
96+
* Check that store id is in global scope
97+
*
98+
* @param int|null $storeId
99+
* @return bool
100+
*/
101+
private function isGlobalScope(int $storeId): bool
102+
{
103+
return null === $storeId || $storeId === Store::DEFAULT_STORE_ID;
104+
}
105+
106+
/**
107+
* @param Category $category
108+
* @param \Magento\Catalog\Model\ResourceModel\Category $categoryResource
109+
*/
110+
private function updateUrlPathForCategory(Category $category, CategoryResource $categoryResource)
111+
{
112+
$category->unsUrlPath();
113+
$category->setUrlPath($this->categoryUrlPathGenerator->getUrlPath($category));
114+
$categoryResource->saveAttribute($category, 'url_path');
115+
}
116+
}
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\CatalogUrlRewrite\Test\Unit\Model\Category\Plugin\Category;
7+
8+
/**
9+
* Unit test for Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\UpdateUrlPath class
10+
*/
11+
class UpdateUrlPathTest extends \PHPUnit\Framework\TestCase
12+
{
13+
/**
14+
* @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
15+
*/
16+
private $objectManager;
17+
18+
/**
19+
* @var \Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator|\PHPUnit_Framework_MockObject_MockObject
20+
*/
21+
private $categoryUrlPathGenerator;
22+
23+
/**
24+
* @var \Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator|\PHPUnit_Framework_MockObject_MockObject
25+
*/
26+
private $categoryUrlRewriteGenerator;
27+
28+
/**
29+
* @var \Magento\CatalogUrlRewrite\Service\V1\StoreViewService|\PHPUnit_Framework_MockObject_MockObject
30+
*/
31+
private $storeViewService;
32+
33+
/**
34+
* @var \Magento\UrlRewrite\Model\UrlPersistInterface|\PHPUnit_Framework_MockObject_MockObject
35+
*/
36+
private $urlPersist;
37+
38+
/**
39+
* @var \Magento\Catalog\Model\ResourceModel\Category|\PHPUnit_Framework_MockObject_MockObject
40+
*/
41+
private $categoryResource;
42+
43+
/**
44+
* @var \Magento\Catalog\Model\Category|\PHPUnit_Framework_MockObject_MockObject
45+
*/
46+
private $category;
47+
48+
/**
49+
* @var \Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\UpdateUrlPath
50+
*/
51+
private $updateUrlPathPlugin;
52+
53+
/**
54+
* @inheritdoc
55+
*/
56+
protected function setUp()
57+
{
58+
$this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
59+
$this->categoryUrlPathGenerator = $this->getMockBuilder(
60+
\Magento\CatalogUrlRewrite\Model\CategoryUrlPathGenerator::class
61+
)
62+
->disableOriginalConstructor()
63+
->setMethods(['getUrlPath'])
64+
->getMock();
65+
$this->categoryUrlRewriteGenerator = $this->getMockBuilder(
66+
\Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator::class
67+
)
68+
->disableOriginalConstructor()
69+
->setMethods(['generate'])
70+
->getMock();
71+
$this->categoryResource = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Category::class)
72+
->disableOriginalConstructor()
73+
->setMethods(['saveAttribute'])
74+
->getMock();
75+
$this->category = $this->getMockBuilder(\Magento\Catalog\Model\Category::class)
76+
->disableOriginalConstructor()
77+
->setMethods(
78+
[
79+
'getStoreId',
80+
'getParentId',
81+
'isObjectNew',
82+
'isInRootCategoryList',
83+
'getStoreIds',
84+
'setStoreId',
85+
'unsUrlPath',
86+
'setUrlPath'
87+
]
88+
)
89+
->getMock();
90+
$this->storeViewService = $this->getMockBuilder(\Magento\CatalogUrlRewrite\Service\V1\StoreViewService::class)
91+
->disableOriginalConstructor()
92+
->setMethods(['doesEntityHaveOverriddenUrlPathForStore'])
93+
->getMock();
94+
$this->urlPersist = $this->getMockBuilder(\Magento\UrlRewrite\Model\UrlPersistInterface::class)
95+
->disableOriginalConstructor()
96+
->setMethods(['replace'])
97+
->getMockForAbstractClass();
98+
99+
$this->updateUrlPathPlugin = $this->objectManager->getObject(
100+
\Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\UpdateUrlPath::class,
101+
[
102+
'categoryUrlPathGenerator' => $this->categoryUrlPathGenerator,
103+
'categoryUrlRewriteGenerator' => $this->categoryUrlRewriteGenerator,
104+
'urlPersist' => $this->urlPersist,
105+
'storeViewService' => $this->storeViewService
106+
]
107+
);
108+
}
109+
110+
public function testAroundSaveWithoutRootCategory()
111+
{
112+
$this->category->expects($this->atLeastOnce())->method('getParentId')->willReturn(0);
113+
$this->category->expects($this->atLeastOnce())->method('isObjectNew')->willReturn(true);
114+
$this->category->expects($this->atLeastOnce())->method('isInRootCategoryList')->willReturn(false);
115+
$this->category->expects($this->never())->method('getStoreIds');
116+
117+
$this->assertEquals(
118+
$this->categoryResource,
119+
$this->updateUrlPathPlugin->afterSave($this->categoryResource, $this->categoryResource, $this->category)
120+
);
121+
}
122+
123+
public function testAroundSaveWithRootCategory()
124+
{
125+
$parentId = 1;
126+
$categoryStoreIds = [0,1,2];
127+
$generatedUrlPath = 'parent_category/child_category';
128+
129+
$this->categoryUrlPathGenerator->expects($this->once())->method('getUrlPath')->with($this->category)
130+
->willReturn($generatedUrlPath);
131+
$this->category->expects($this->atLeastOnce())->method('getParentId')->willReturn($parentId);
132+
$this->category->expects($this->atLeastOnce())->method('isObjectNew')->willReturn(true);
133+
$this->category->expects($this->atLeastOnce())->method('isInRootCategoryList')->willReturn(false);
134+
$this->category->expects($this->atLeastOnce())->method('getStoreIds')->willReturn($categoryStoreIds);
135+
$this->category->expects($this->once())->method('setStoreId')->with($categoryStoreIds[2])->willReturnSelf();
136+
$this->category->expects($this->once())->method('unsUrlPath')->willReturnSelf();
137+
$this->category->expects($this->once())->method('setUrlPath')->with($generatedUrlPath)->willReturnSelf();
138+
$this->storeViewService->expects($this->exactly(2))->method('doesEntityHaveOverriddenUrlPathForStore')
139+
->willReturnMap(
140+
[
141+
[
142+
$categoryStoreIds[1], $parentId, 'catalog_category', false
143+
],
144+
[
145+
$categoryStoreIds[2], $parentId, 'catalog_category', true
146+
]
147+
]
148+
);
149+
$this->categoryResource->expects($this->once())->method('saveAttribute')->with($this->category, 'url_path')
150+
->willReturnSelf();
151+
$generatedUrlRewrite = $this->getMockBuilder(\Magento\UrlRewrite\Service\V1\Data\UrlRewrite::class)
152+
->disableOriginalConstructor()
153+
->getMock();
154+
$this->categoryUrlRewriteGenerator->expects($this->once())->method('generate')->with($this->category)
155+
->willReturn([$generatedUrlRewrite]);
156+
$this->urlPersist->expects($this->once())->method('replace')->with([$generatedUrlRewrite])->willReturnSelf();
157+
158+
$this->assertEquals(
159+
$this->categoryResource,
160+
$this->updateUrlPathPlugin->afterSave($this->categoryResource, $this->categoryResource, $this->category)
161+
);
162+
}
163+
}

app/code/Magento/CatalogUrlRewrite/etc/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
<type name="Magento\Catalog\Model\ResourceModel\Category">
2020
<plugin name="category_move_plugin" type="Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\Move"/>
2121
<plugin name="category_delete_plugin" type="Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\Remove"/>
22+
<plugin name="update_url_path_for_different_stores" type="Magento\CatalogUrlRewrite\Model\Category\Plugin\Category\UpdateUrlPath"/>
2223
</type>
2324
<type name="Magento\UrlRewrite\Model\StorageInterface">
2425
<plugin name="storage_plugin" type="Magento\CatalogUrlRewrite\Model\Category\Plugin\Storage"/>

dev/tests/integration/testsuite/Magento/Catalog/Model/CategoryTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,43 @@ public function testDeleteChildren()
320320
$this->assertEquals($this->_model->getId(), null);
321321
}
322322

323+
/**
324+
* @magentoDataFixture Magento/Store/_files/second_store.php
325+
* @magentoDataFixture Magento/Catalog/_files/categories.php
326+
* @magentoDbIsolation enabled
327+
*/
328+
public function testCreateSubcategoryWithMultipleStores()
329+
{
330+
$parentCategoryId = 3;
331+
$storeManager = $this->objectManager->get(\Magento\Store\Model\StoreManagerInterface::class);
332+
$storeManager->setCurrentStore(\Magento\Store\Model\Store::ADMIN_CODE);
333+
/** @var \Magento\Store\Api\StoreRepositoryInterface $storeRepository */
334+
$storeRepository = $this->objectManager->get(\Magento\Store\Api\StoreRepositoryInterface::class);
335+
$storeId = $storeRepository->get('fixture_second_store')->getId();
336+
/** @var \Magento\Catalog\Api\CategoryRepositoryInterface $repository */
337+
$repository = $this->objectManager->get(\Magento\Catalog\Api\CategoryRepositoryInterface::class);
338+
$parentCategory = $repository->get($parentCategoryId, $storeId);
339+
$parentAllStoresPath = $parentCategory->getUrlPath();
340+
$parentSecondStoreKey = 'parent-category-url-key-second-store';
341+
$parentCategory->setUrlKey($parentSecondStoreKey);
342+
$repository->save($parentCategory);
343+
/** @var \Magento\Catalog\Model\Category $childCategory */
344+
$childCategory = $this->objectManager->create(\Magento\Catalog\Model\Category::class);
345+
$childCategory->setName('Test Category 100')
346+
->setParentId($parentCategoryId)
347+
->setLevel(2)
348+
->setAvailableSortBy(['position', 'name'])
349+
->setDefaultSortBy('name')
350+
->setIsActive(true)
351+
->setPosition(1)
352+
->isObjectNew(true);
353+
$repository->save($childCategory);
354+
$childCategorySecondStore = $repository->get($childCategory->getId(), $storeId);
355+
356+
$this->assertEquals($parentAllStoresPath . '/test-category-100', $childCategory->getUrlPath());
357+
$this->assertEquals($parentSecondStoreKey . '/test-category-100', $childCategorySecondStore->getUrlPath());
358+
}
359+
323360
protected function getCategoryByName($categoryName)
324361
{
325362
/* @var \Magento\Catalog\Model\ResourceModel\Category\Collection $collection */

0 commit comments

Comments
 (0)