Skip to content

Commit 6fc1e3c

Browse files
MC-32273: Part of URL is missed after saving category image
1 parent a66a2d5 commit 6fc1e3c

File tree

9 files changed

+357
-13
lines changed

9 files changed

+357
-13
lines changed

app/code/Magento/Catalog/Model/Category/DataProvider.php

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
* @api
4242
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
4343
* @SuppressWarnings(PHPMD.TooManyFields)
44+
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
4445
* @since 101.0.0
4546
*/
4647
class DataProvider extends ModifierPoolDataProvider
@@ -176,6 +177,10 @@ class DataProvider extends ModifierPoolDataProvider
176177
* @var AuthorizationInterface
177178
*/
178179
private $auth;
180+
/**
181+
* @var Image
182+
*/
183+
private $categoryImage;
179184

180185
/**
181186
* @param string $name
@@ -196,6 +201,7 @@ class DataProvider extends ModifierPoolDataProvider
196201
* @param ScopeOverriddenValue|null $scopeOverriddenValue
197202
* @param ArrayManager|null $arrayManager
198203
* @param FileInfo|null $fileInfo
204+
* @param Image|null $categoryImage
199205
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
200206
*/
201207
public function __construct(
@@ -216,7 +222,8 @@ public function __construct(
216222
?ArrayUtils $arrayUtils = null,
217223
ScopeOverriddenValue $scopeOverriddenValue = null,
218224
ArrayManager $arrayManager = null,
219-
FileInfo $fileInfo = null
225+
FileInfo $fileInfo = null,
226+
?Image $categoryImage = null
220227
) {
221228
$this->eavValidationRules = $eavValidationRules;
222229
$this->collection = $categoryCollectionFactory->create();
@@ -232,6 +239,7 @@ public function __construct(
232239
ObjectManager::getInstance()->get(ScopeOverriddenValue::class);
233240
$this->arrayManager = $arrayManager ?: ObjectManager::getInstance()->get(ArrayManager::class);
234241
$this->fileInfo = $fileInfo ?: ObjectManager::getInstance()->get(FileInfo::class);
242+
$this->categoryImage = $categoryImage ?? ObjectManager::getInstance()->get(Image::class);
235243

236244
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data, $pool);
237245
}
@@ -601,11 +609,7 @@ private function convertValues($category, $categoryData): array
601609
// phpcs:ignore Magento2.Functions.DiscouragedFunction
602610
$categoryData[$attributeCode][0]['name'] = basename($fileName);
603611

604-
if ($this->fileInfo->isBeginsWithMediaDirectoryPath($fileName)) {
605-
$categoryData[$attributeCode][0]['url'] = $fileName;
606-
} else {
607-
$categoryData[$attributeCode][0]['url'] = $category->getImageUrl($attributeCode);
608-
}
612+
$categoryData[$attributeCode][0]['url'] = $this->categoryImage->getUrl($category, $attributeCode);
609613

610614
$categoryData[$attributeCode][0]['size'] = isset($stat) ? $stat['size'] : 0;
611615
$categoryData[$attributeCode][0]['type'] = $mime;

app/code/Magento/Catalog/Model/Category/FileInfo.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,4 +245,15 @@ private function getMediaDirectoryPathRelativeToBaseDirectoryPath(string $filePa
245245

246246
return $mediaDirectoryRelativeSubpath;
247247
}
248+
249+
/**
250+
* Get file relative path to media directory
251+
*
252+
* @param string $filename
253+
* @return string
254+
*/
255+
public function getRelativePathToMediaDirectory(string $filename): string
256+
{
257+
return $this->getFilePath($filename);
258+
}
248259
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Category;
9+
10+
use Magento\Catalog\Model\Category;
11+
use Magento\Framework\Exception\LocalizedException;
12+
use Magento\Framework\UrlInterface;
13+
use Magento\Store\Model\StoreManagerInterface;
14+
15+
/**
16+
* Category Image Service
17+
*/
18+
class Image
19+
{
20+
private const ATTRIBUTE_NAME = 'image';
21+
/**
22+
* @var FileInfo
23+
*/
24+
private $fileInfo;
25+
/**
26+
* @var StoreManagerInterface
27+
*/
28+
private $storeManager;
29+
30+
/**
31+
* Initialize dependencies.
32+
*
33+
* @param FileInfo $fileInfo
34+
* @param StoreManagerInterface $storeManager
35+
*/
36+
public function __construct(
37+
FileInfo $fileInfo,
38+
StoreManagerInterface $storeManager
39+
) {
40+
$this->fileInfo = $fileInfo;
41+
$this->storeManager = $storeManager;
42+
}
43+
/**
44+
* Resolve category image URL
45+
*
46+
* @param Category $category
47+
* @param string $attributeCode
48+
* @return string
49+
* @throws LocalizedException
50+
*/
51+
public function getUrl(Category $category, string $attributeCode = self::ATTRIBUTE_NAME): string
52+
{
53+
$url = '';
54+
$image = $category->getData($attributeCode);
55+
if ($image) {
56+
if (is_string($image)) {
57+
$store = $this->storeManager->getStore();
58+
$mediaBaseUrl = $store->getBaseUrl(UrlInterface::URL_TYPE_MEDIA);
59+
if ($this->fileInfo->isBeginsWithMediaDirectoryPath($image)) {
60+
$relativePath = $this->fileInfo->getRelativePathToMediaDirectory($image);
61+
$url = rtrim($mediaBaseUrl, '/') . '/' . ltrim($relativePath, '/');
62+
} elseif (substr($image, 0, 1) !== '/') {
63+
$url = rtrim($mediaBaseUrl, '/') . '/' . ltrim(FileInfo::ENTITY_MEDIA_PATH, '/') . '/' . $image;
64+
} else {
65+
$url = $image;
66+
}
67+
} else {
68+
throw new LocalizedException(
69+
__('Something went wrong while getting the image url.')
70+
);
71+
}
72+
}
73+
return $url;
74+
}
75+
}

app/code/Magento/Catalog/Test/Unit/Model/Category/DataProviderTest.php

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Magento\Catalog\Model\Category\Attribute\Backend\Image;
1111
use Magento\Catalog\Model\Category\DataProvider;
1212
use Magento\Catalog\Model\Category\FileInfo;
13+
use Magento\Catalog\Model\Category\Image as CategoryImage;
1314
use Magento\Catalog\Model\CategoryFactory;
1415
use Magento\Catalog\Model\ResourceModel\Category\Collection;
1516
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
@@ -98,6 +99,11 @@ class DataProviderTest extends TestCase
9899
*/
99100
private $auth;
100101

102+
/**
103+
* @var CategoryImage|MockObject
104+
*/
105+
private $categoryImage;
106+
101107
/**
102108
* @inheritDoc
103109
*/
@@ -155,6 +161,11 @@ protected function setUp()
155161
$this->arrayUtils = $this->getMockBuilder(ArrayUtils::class)
156162
->setMethods(['flatten'])
157163
->disableOriginalConstructor()->getMock();
164+
165+
$this->categoryImage = $this->createPartialMock(
166+
CategoryImage::class,
167+
['getUrl']
168+
);
158169
}
159170

160171
/**
@@ -185,7 +196,8 @@ private function getModel()
185196
'categoryFactory' => $this->categoryFactory,
186197
'pool' => $this->modifierPool,
187198
'auth' => $this->auth,
188-
'arrayUtils' => $this->arrayUtils
199+
'arrayUtils' => $this->arrayUtils,
200+
'categoryImage' => $this->categoryImage,
189201
]
190202
);
191203

@@ -324,8 +336,8 @@ public function testGetData()
324336
$categoryMock->expects($this->once())
325337
->method('getAttributes')
326338
->willReturn(['image' => $attributeMock]);
327-
$categoryMock->expects($this->once())
328-
->method('getImageUrl')
339+
$this->categoryImage->expects($this->once())
340+
->method('getUrl')
329341
->willReturn($categoryUrl);
330342

331343
$this->registry->expects($this->once())
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Test\Unit\Model\Category;
9+
10+
use Magento\Catalog\Model\Category;
11+
use Magento\Catalog\Model\Category\FileInfo;
12+
use Magento\Catalog\Model\Category\Image;
13+
use Magento\Framework\TestFramework\Unit\Helper\ObjectManager;
14+
use Magento\Framework\UrlInterface;
15+
use Magento\Store\Model\Store;
16+
use Magento\Store\Model\StoreManager;
17+
use PHPUnit\Framework\MockObject\MockObject;
18+
use PHPUnit\Framework\TestCase;
19+
20+
/**
21+
* Test category image resolver
22+
*/
23+
class ImageTest extends TestCase
24+
{
25+
/**
26+
* @var Store|MockObject
27+
*/
28+
private $store;
29+
/**
30+
* @var Category
31+
*/
32+
private $category;
33+
/**
34+
* @var Image
35+
*/
36+
private $model;
37+
38+
/**
39+
* @inheritDoc
40+
*/
41+
protected function setUp()
42+
{
43+
$storeManager = $this->createPartialMock(StoreManager::class, ['getStore']);
44+
$this->store = $this->createPartialMock(Store::class, ['getBaseUrl']);
45+
$storeManager->method('getStore')->willReturn($this->store);
46+
$objectManager = new ObjectManager($this);
47+
$this->category = $objectManager->getObject(Category::class);
48+
$this->model = $objectManager->getObject(
49+
Image::class,
50+
[
51+
'storeManager' => $storeManager,
52+
'fileInfo' => $this->getFileInfo()
53+
]
54+
);
55+
}
56+
57+
/**
58+
* Test that image URL resolver works correctly with different base URL format
59+
*
60+
* @param string $baseUrl
61+
* @param string $imagePath
62+
* @param string $url
63+
* @dataProvider getUrlDataProvider
64+
*/
65+
public function testGetUrl(string $imagePath, string $baseUrl, string $url)
66+
{
67+
$this->store->method('getBaseUrl')
68+
->with(UrlInterface::URL_TYPE_MEDIA)
69+
->willReturn($baseUrl);
70+
$this->category->setData('image_attr_code', $imagePath);
71+
$this->assertEquals($url, $this->model->getUrl($this->category, 'image_attr_code'));
72+
}
73+
74+
/**
75+
* @return array
76+
*/
77+
public function getUrlDataProvider()
78+
{
79+
return [
80+
[
81+
'testimage',
82+
'http://www.example.com/',
83+
'http://www.example.com/catalog/category/testimage'
84+
],
85+
[
86+
'testimage',
87+
'http://www.example.com/pub/media/',
88+
'http://www.example.com/pub/media/catalog/category/testimage'
89+
],
90+
[
91+
'testimage',
92+
'http://www.example.com/base/path/pub/media/',
93+
'http://www.example.com/base/path/pub/media/catalog/category/testimage'
94+
],
95+
[
96+
'/pub/media/catalog/category/testimage',
97+
'http://www.example.com/pub/media/',
98+
'http://www.example.com/pub/media/catalog/category/testimage'
99+
],
100+
[
101+
'/pub/media/catalog/category/testimage',
102+
'http://www.example.com/base/path/pub/media/',
103+
'http://www.example.com/base/path/pub/media/catalog/category/testimage'
104+
],
105+
[
106+
'/pub/media/posters/testimage',
107+
'http://www.example.com/pub/media/',
108+
'http://www.example.com/pub/media/posters/testimage'
109+
],
110+
[
111+
'/pub/media/posters/testimage',
112+
'http://www.example.com/base/path/pub/media/',
113+
'http://www.example.com/base/path/pub/media/posters/testimage'
114+
],
115+
[
116+
'',
117+
'http://www.example.com/',
118+
''
119+
]
120+
];
121+
}
122+
123+
/**
124+
* Get FileInfo mock
125+
*
126+
* @return MockObject
127+
*/
128+
private function getFileInfo(): MockObject
129+
{
130+
$mediaDir = 'pub/media';
131+
$fileInfo = $this->createMock(FileInfo::class);
132+
$fileInfo->method('isBeginsWithMediaDirectoryPath')
133+
->willReturnCallback(
134+
function ($path) use ($mediaDir) {
135+
return strpos(ltrim($path, '/'), $mediaDir) === 0;
136+
}
137+
);
138+
$fileInfo->method('getRelativePathToMediaDirectory')
139+
->willReturnCallback(
140+
function ($path) use ($mediaDir) {
141+
return str_replace($mediaDir, '', $path);
142+
}
143+
);
144+
return $fileInfo;
145+
}
146+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\ViewModel\Category;
9+
10+
use Magento\Catalog\Model\Category;
11+
use Magento\Catalog\Model\Category\Image as CategoryImage;
12+
use Magento\Framework\View\Element\Block\ArgumentInterface;
13+
14+
/**
15+
* Category image view model
16+
*/
17+
class Image implements ArgumentInterface
18+
{
19+
private const ATTRIBUTE_NAME = 'image';
20+
/**
21+
* @var CategoryImage
22+
*/
23+
private $image;
24+
25+
/**
26+
* Initialize dependencies.
27+
*
28+
* @param CategoryImage $image
29+
*/
30+
public function __construct(CategoryImage $image)
31+
{
32+
$this->image = $image;
33+
}
34+
35+
/**
36+
* Resolve category image URL
37+
*
38+
* @param Category $category
39+
* @param string $attributeCode
40+
* @return string
41+
*/
42+
public function getUrl(Category $category, string $attributeCode = self::ATTRIBUTE_NAME): string
43+
{
44+
return $this->image->getUrl($category, $attributeCode);
45+
}
46+
}

0 commit comments

Comments
 (0)