Skip to content

Commit ee095ec

Browse files
ENGCOM-5173: Improve command catalog:images:resize #23005
2 parents 0f29a26 + 4a3cbf8 commit ee095ec

File tree

4 files changed

+225
-26
lines changed

4 files changed

+225
-26
lines changed

app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function __construct(
5454
}
5555

5656
/**
57-
* Returns product images
57+
* Get all product images.
5858
*
5959
* @return \Generator
6060
*/
@@ -75,7 +75,28 @@ public function getAllProductImages(): \Generator
7575
}
7676

7777
/**
78-
* Get the number of unique pictures of products
78+
* Get used product images.
79+
*
80+
* @return \Generator
81+
*/
82+
public function getUsedProductImages(): \Generator
83+
{
84+
$batchSelectIterator = $this->batchQueryGenerator->generate(
85+
'value_id',
86+
$this->getUsedImagesSelect(),
87+
$this->batchSize,
88+
\Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR
89+
);
90+
91+
foreach ($batchSelectIterator as $select) {
92+
foreach ($this->connection->fetchAll($select) as $key => $value) {
93+
yield $key => $value;
94+
}
95+
}
96+
}
97+
98+
/**
99+
* Get the number of unique images of products.
79100
*
80101
* @return int
81102
*/
@@ -92,7 +113,24 @@ public function getCountAllProductImages(): int
92113
}
93114

94115
/**
95-
* Return Select to fetch all products images
116+
* Get the number of unique and used images of products.
117+
*
118+
* @return int
119+
*/
120+
public function getCountUsedProductImages(): int
121+
{
122+
$select = $this->getUsedImagesSelect()
123+
->reset('columns')
124+
->reset('distinct')
125+
->columns(
126+
new \Zend_Db_Expr('count(distinct value)')
127+
);
128+
129+
return (int) $this->connection->fetchOne($select);
130+
}
131+
132+
/**
133+
* Return select to fetch all products images.
96134
*
97135
* @return Select
98136
*/
@@ -106,4 +144,23 @@ private function getVisibleImagesSelect(): Select
106144
'disabled = 0'
107145
);
108146
}
147+
148+
/**
149+
* Return select to fetch all used product images.
150+
*
151+
* @return Select
152+
*/
153+
private function getUsedImagesSelect(): Select
154+
{
155+
return $this->connection->select()->distinct()
156+
->from(
157+
['images' => $this->resourceConnection->getTableName(Gallery::GALLERY_TABLE)],
158+
'value as filepath'
159+
)->joinInner(
160+
['image_value' => $this->resourceConnection->getTableName(Gallery::GALLERY_VALUE_TABLE)],
161+
'images.value_id = image_value.value_id'
162+
)->where(
163+
'images.disabled = 0 AND image_value.disabled = 0'
164+
);
165+
}
109166
}

app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php

Lines changed: 125 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
use PHPUnit_Framework_MockObject_MockObject as MockObject;
1717
use Magento\Framework\DB\Query\BatchIteratorInterface;
1818

19+
/**
20+
* Class ImageTest
21+
* @package Magento\Catalog\Test\Unit\Model\ResourceModel\Product
22+
*/
1923
class ImageTest extends \PHPUnit\Framework\TestCase
2024
{
2125
/**
@@ -76,6 +80,37 @@ protected function getVisibleImagesSelectMock(): MockObject
7680
return $selectMock;
7781
}
7882

83+
/**
84+
* @return MockObject
85+
*/
86+
protected function getUsedImagesSelectMock(): MockObject
87+
{
88+
$selectMock = $this->getMockBuilder(Select::class)
89+
->disableOriginalConstructor()
90+
->getMock();
91+
$selectMock->expects($this->once())
92+
->method('distinct')
93+
->willReturnSelf();
94+
$selectMock->expects($this->once())
95+
->method('from')
96+
->with(
97+
['images' => Gallery::GALLERY_TABLE],
98+
'value as filepath'
99+
)->willReturnSelf();
100+
$selectMock->expects($this->once())
101+
->method('joinInner')
102+
->with(
103+
['image_value' => Gallery::GALLERY_VALUE_TABLE],
104+
'images.value_id = image_value.value_id'
105+
)->willReturnSelf();
106+
$selectMock->expects($this->once())
107+
->method('where')
108+
->with('images.disabled = 0 AND image_value.disabled = 0')
109+
->willReturnSelf();
110+
111+
return $selectMock;
112+
}
113+
79114
/**
80115
* @param int $imagesCount
81116
* @dataProvider dataProvider
@@ -116,15 +151,53 @@ public function testGetCountAllProductImages(int $imagesCount): void
116151
);
117152
}
118153

154+
/**
155+
* @param int $imagesCount
156+
* @dataProvider dataProvider
157+
*/
158+
public function testGetCountUsedProductImages(int $imagesCount): void
159+
{
160+
$selectMock = $this->getUsedImagesSelectMock();
161+
$selectMock->expects($this->exactly(2))
162+
->method('reset')
163+
->withConsecutive(
164+
['columns'],
165+
['distinct']
166+
)->willReturnSelf();
167+
$selectMock->expects($this->once())
168+
->method('columns')
169+
->with(new \Zend_Db_Expr('count(distinct value)'))
170+
->willReturnSelf();
171+
172+
$this->connectionMock->expects($this->once())
173+
->method('select')
174+
->willReturn($selectMock);
175+
$this->connectionMock->expects($this->once())
176+
->method('fetchOne')
177+
->with($selectMock)
178+
->willReturn($imagesCount);
179+
180+
$imageModel = $this->objectManager->getObject(
181+
Image::class,
182+
[
183+
'generator' => $this->generatorMock,
184+
'resourceConnection' => $this->resourceMock
185+
]
186+
);
187+
188+
$this->assertSame(
189+
$imagesCount,
190+
$imageModel->getCountUsedProductImages()
191+
);
192+
}
193+
119194
/**
120195
* @param int $imagesCount
121196
* @param int $batchSize
122197
* @dataProvider dataProvider
123198
*/
124-
public function testGetAllProductImages(
125-
int $imagesCount,
126-
int $batchSize
127-
): void {
199+
public function testGetAllProductImages(int $imagesCount, int $batchSize): void
200+
{
128201
$this->connectionMock->expects($this->once())
129202
->method('select')
130203
->willReturn($this->getVisibleImagesSelectMock());
@@ -165,6 +238,54 @@ public function testGetAllProductImages(
165238
$this->assertCount($imagesCount, $imageModel->getAllProductImages());
166239
}
167240

241+
/**
242+
* @param int $imagesCount
243+
* @param int $batchSize
244+
* @dataProvider dataProvider
245+
*/
246+
public function testGetUsedProductImages(int $imagesCount, int $batchSize): void
247+
{
248+
$this->connectionMock->expects($this->once())
249+
->method('select')
250+
->willReturn($this->getUsedImagesSelectMock());
251+
252+
$batchCount = (int)ceil($imagesCount / $batchSize);
253+
$fetchResultsCallback = $this->getFetchResultCallbackForBatches($imagesCount, $batchSize);
254+
$this->connectionMock->expects($this->exactly($batchCount))
255+
->method('fetchAll')
256+
->will($this->returnCallback($fetchResultsCallback));
257+
258+
/** @var Select | MockObject $selectMock */
259+
$selectMock = $this->getMockBuilder(Select::class)
260+
->disableOriginalConstructor()
261+
->getMock();
262+
263+
$this->generatorMock->expects($this->once())
264+
->method('generate')
265+
->with(
266+
'value_id',
267+
$selectMock,
268+
$batchSize,
269+
BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR
270+
)->will(
271+
$this->returnCallback(
272+
$this->getBatchIteratorCallback($selectMock, $batchCount)
273+
)
274+
);
275+
276+
/** @var Image $imageModel */
277+
$imageModel = $this->objectManager->getObject(
278+
Image::class,
279+
[
280+
'generator' => $this->generatorMock,
281+
'resourceConnection' => $this->resourceMock,
282+
'batchSize' => $batchSize
283+
]
284+
);
285+
286+
$this->assertCount($imagesCount, $imageModel->getUsedProductImages());
287+
}
288+
168289
/**
169290
* @param int $imagesCount
170291
* @param int $batchSize

app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,20 @@
88
namespace Magento\MediaStorage\Console\Command;
99

1010
use Magento\Framework\App\Area;
11+
use Magento\Framework\App\ObjectManager;
1112
use Magento\Framework\App\State;
13+
use Magento\Framework\ObjectManagerInterface;
1214
use Magento\MediaStorage\Service\ImageResize;
15+
use Symfony\Component\Console\Helper\ProgressBar;
16+
use Symfony\Component\Console\Helper\ProgressBarFactory;
1317
use Symfony\Component\Console\Input\InputInterface;
1418
use Symfony\Component\Console\Output\OutputInterface;
15-
use Symfony\Component\Console\Helper\ProgressBar;
16-
use Magento\Framework\ObjectManagerInterface;
1719

20+
/**
21+
* Resizes product images according to theme view definitions.
22+
*
23+
* @package Magento\MediaStorage\Console\Command
24+
*/
1825
class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command
1926
{
2027
/**
@@ -28,28 +35,32 @@ class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command
2835
private $appState;
2936

3037
/**
31-
* @var ObjectManagerInterface
38+
* @var ProgressBarFactory
3239
*/
33-
private $objectManager;
40+
private $progressBarFactory;
3441

3542
/**
3643
* @param State $appState
3744
* @param ImageResize $resize
3845
* @param ObjectManagerInterface $objectManager
46+
* @param ProgressBarFactory $progressBarFactory
47+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
3948
*/
4049
public function __construct(
4150
State $appState,
4251
ImageResize $resize,
43-
ObjectManagerInterface $objectManager
52+
ObjectManagerInterface $objectManager,
53+
ProgressBarFactory $progressBarFactory = null
4454
) {
4555
parent::__construct();
4656
$this->resize = $resize;
4757
$this->appState = $appState;
48-
$this->objectManager = $objectManager;
58+
$this->progressBarFactory = $progressBarFactory
59+
?: ObjectManager::getInstance()->get(ProgressBarFactory::class);
4960
}
5061

5162
/**
52-
* {@inheritdoc}
63+
* @inheritdoc
5364
*/
5465
protected function configure()
5566
{
@@ -58,7 +69,9 @@ protected function configure()
5869
}
5970

6071
/**
61-
* {@inheritdoc}
72+
* @inheritdoc
73+
* @param InputInterface $input
74+
* @param OutputInterface $output
6275
*/
6376
protected function execute(InputInterface $input, OutputInterface $output)
6477
{
@@ -67,10 +80,12 @@ protected function execute(InputInterface $input, OutputInterface $output)
6780
$generator = $this->resize->resizeFromThemes();
6881

6982
/** @var ProgressBar $progress */
70-
$progress = $this->objectManager->create(ProgressBar::class, [
71-
'output' => $output,
72-
'max' => $generator->current()
73-
]);
83+
$progress = $this->progressBarFactory->create(
84+
[
85+
'output' => $output,
86+
'max' => $generator->current()
87+
]
88+
);
7489
$progress->setFormat(
7590
"%current%/%max% [%bar%] %percent:3s%% %elapsed% %memory:6s% \t| <info>%message%</info>"
7691
);
@@ -79,9 +94,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
7994
$progress->setOverwrite(false);
8095
}
8196

82-
for (; $generator->valid(); $generator->next()) {
97+
while ($generator->valid()) {
8398
$progress->setMessage($generator->key());
8499
$progress->advance();
100+
$generator->next();
85101
}
86102
} catch (\Exception $e) {
87103
$output->writeln("<error>{$e->getMessage()}</error>");
@@ -91,5 +107,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
91107

92108
$output->write(PHP_EOL);
93109
$output->writeln("<info>Product images resized successfully</info>");
110+
111+
return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
94112
}
95113
}

app/code/Magento/MediaStorage/Service/ImageResize.php

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,12 @@ public function resizeFromImageName(string $originalImageName)
152152
*/
153153
public function resizeFromThemes(array $themes = null): \Generator
154154
{
155-
$count = $this->productImage->getCountAllProductImages();
155+
$count = $this->productImage->getCountUsedProductImages();
156156
if (!$count) {
157157
throw new NotFoundException(__('Cannot resize images - product images not found'));
158158
}
159159

160-
$productImages = $this->productImage->getAllProductImages();
160+
$productImages = $this->productImage->getUsedProductImages();
161161
$viewImages = $this->getViewImages($themes ?? $this->getThemesInUse());
162162

163163
foreach ($productImages as $image) {
@@ -202,10 +202,12 @@ private function getViewImages(array $themes): array
202202
$viewImages = [];
203203
/** @var \Magento\Theme\Model\Theme $theme */
204204
foreach ($themes as $theme) {
205-
$config = $this->viewConfig->getViewConfig([
206-
'area' => Area::AREA_FRONTEND,
207-
'themeModel' => $theme,
208-
]);
205+
$config = $this->viewConfig->getViewConfig(
206+
[
207+
'area' => Area::AREA_FRONTEND,
208+
'themeModel' => $theme,
209+
]
210+
);
209211
$images = $config->getMediaEntities('Magento_Catalog', ImageHelper::MEDIA_TYPE_CONFIG_NODE);
210212
foreach ($images as $imageId => $imageData) {
211213
$uniqIndex = $this->getUniqueImageIndex($imageData);
@@ -226,6 +228,7 @@ private function getUniqueImageIndex(array $imageData): string
226228
{
227229
ksort($imageData);
228230
unset($imageData['type']);
231+
// phpcs:disable Magento2.Security.InsecureFunction
229232
return md5(json_encode($imageData));
230233
}
231234

0 commit comments

Comments
 (0)