diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
index 77f67480619e0..febffde3b75a7 100644
--- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
+++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php
@@ -54,7 +54,7 @@ public function __construct(
}
/**
- * Returns product images
+ * Get all product images.
*
* @return \Generator
*/
@@ -75,7 +75,28 @@ public function getAllProductImages(): \Generator
}
/**
- * Get the number of unique pictures of products
+ * Get used product images.
+ *
+ * @return \Generator
+ */
+ public function getUsedProductImages(): \Generator
+ {
+ $batchSelectIterator = $this->batchQueryGenerator->generate(
+ 'value_id',
+ $this->getUsedImagesSelect(),
+ $this->batchSize,
+ \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR
+ );
+
+ foreach ($batchSelectIterator as $select) {
+ foreach ($this->connection->fetchAll($select) as $key => $value) {
+ yield $key => $value;
+ }
+ }
+ }
+
+ /**
+ * Get the number of unique images of products.
*
* @return int
*/
@@ -92,7 +113,24 @@ public function getCountAllProductImages(): int
}
/**
- * Return Select to fetch all products images
+ * Get the number of unique and used images of products.
+ *
+ * @return int
+ */
+ public function getCountUsedProductImages(): int
+ {
+ $select = $this->getUsedImagesSelect()
+ ->reset('columns')
+ ->reset('distinct')
+ ->columns(
+ new \Zend_Db_Expr('count(distinct value)')
+ );
+
+ return (int) $this->connection->fetchOne($select);
+ }
+
+ /**
+ * Return select to fetch all products images.
*
* @return Select
*/
@@ -106,4 +144,23 @@ private function getVisibleImagesSelect(): Select
'disabled = 0'
);
}
+
+ /**
+ * Return select to fetch all used product images.
+ *
+ * @return Select
+ */
+ private function getUsedImagesSelect(): Select
+ {
+ return $this->connection->select()->distinct()
+ ->from(
+ ['images' => $this->resourceConnection->getTableName(Gallery::GALLERY_TABLE)],
+ 'value as filepath'
+ )->joinInner(
+ ['image_value' => $this->resourceConnection->getTableName(Gallery::GALLERY_VALUE_TABLE)],
+ 'images.value_id = image_value.value_id'
+ )->where(
+ 'images.disabled = 0 AND image_value.disabled = 0'
+ );
+ }
}
diff --git a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php
index 4fce12dc2de89..af2cb6f06ed5a 100644
--- a/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php
+++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php
@@ -16,6 +16,10 @@
use PHPUnit_Framework_MockObject_MockObject as MockObject;
use Magento\Framework\DB\Query\BatchIteratorInterface;
+/**
+ * Class ImageTest
+ * @package Magento\Catalog\Test\Unit\Model\ResourceModel\Product
+ */
class ImageTest extends \PHPUnit\Framework\TestCase
{
/**
@@ -76,6 +80,37 @@ protected function getVisibleImagesSelectMock(): MockObject
return $selectMock;
}
+ /**
+ * @return MockObject
+ */
+ protected function getUsedImagesSelectMock(): MockObject
+ {
+ $selectMock = $this->getMockBuilder(Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $selectMock->expects($this->once())
+ ->method('distinct')
+ ->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('from')
+ ->with(
+ ['images' => Gallery::GALLERY_TABLE],
+ 'value as filepath'
+ )->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('joinInner')
+ ->with(
+ ['image_value' => Gallery::GALLERY_VALUE_TABLE],
+ 'images.value_id = image_value.value_id'
+ )->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('where')
+ ->with('images.disabled = 0 AND image_value.disabled = 0')
+ ->willReturnSelf();
+
+ return $selectMock;
+ }
+
/**
* @param int $imagesCount
* @dataProvider dataProvider
@@ -116,15 +151,53 @@ public function testGetCountAllProductImages(int $imagesCount): void
);
}
+ /**
+ * @param int $imagesCount
+ * @dataProvider dataProvider
+ */
+ public function testGetCountUsedProductImages(int $imagesCount): void
+ {
+ $selectMock = $this->getUsedImagesSelectMock();
+ $selectMock->expects($this->exactly(2))
+ ->method('reset')
+ ->withConsecutive(
+ ['columns'],
+ ['distinct']
+ )->willReturnSelf();
+ $selectMock->expects($this->once())
+ ->method('columns')
+ ->with(new \Zend_Db_Expr('count(distinct value)'))
+ ->willReturnSelf();
+
+ $this->connectionMock->expects($this->once())
+ ->method('select')
+ ->willReturn($selectMock);
+ $this->connectionMock->expects($this->once())
+ ->method('fetchOne')
+ ->with($selectMock)
+ ->willReturn($imagesCount);
+
+ $imageModel = $this->objectManager->getObject(
+ Image::class,
+ [
+ 'generator' => $this->generatorMock,
+ 'resourceConnection' => $this->resourceMock
+ ]
+ );
+
+ $this->assertSame(
+ $imagesCount,
+ $imageModel->getCountUsedProductImages()
+ );
+ }
+
/**
* @param int $imagesCount
* @param int $batchSize
* @dataProvider dataProvider
*/
- public function testGetAllProductImages(
- int $imagesCount,
- int $batchSize
- ): void {
+ public function testGetAllProductImages(int $imagesCount, int $batchSize): void
+ {
$this->connectionMock->expects($this->once())
->method('select')
->willReturn($this->getVisibleImagesSelectMock());
@@ -165,6 +238,54 @@ public function testGetAllProductImages(
$this->assertCount($imagesCount, $imageModel->getAllProductImages());
}
+ /**
+ * @param int $imagesCount
+ * @param int $batchSize
+ * @dataProvider dataProvider
+ */
+ public function testGetUsedProductImages(int $imagesCount, int $batchSize): void
+ {
+ $this->connectionMock->expects($this->once())
+ ->method('select')
+ ->willReturn($this->getUsedImagesSelectMock());
+
+ $batchCount = (int)ceil($imagesCount / $batchSize);
+ $fetchResultsCallback = $this->getFetchResultCallbackForBatches($imagesCount, $batchSize);
+ $this->connectionMock->expects($this->exactly($batchCount))
+ ->method('fetchAll')
+ ->will($this->returnCallback($fetchResultsCallback));
+
+ /** @var Select | MockObject $selectMock */
+ $selectMock = $this->getMockBuilder(Select::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->generatorMock->expects($this->once())
+ ->method('generate')
+ ->with(
+ 'value_id',
+ $selectMock,
+ $batchSize,
+ BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR
+ )->will(
+ $this->returnCallback(
+ $this->getBatchIteratorCallback($selectMock, $batchCount)
+ )
+ );
+
+ /** @var Image $imageModel */
+ $imageModel = $this->objectManager->getObject(
+ Image::class,
+ [
+ 'generator' => $this->generatorMock,
+ 'resourceConnection' => $this->resourceMock,
+ 'batchSize' => $batchSize
+ ]
+ );
+
+ $this->assertCount($imagesCount, $imageModel->getUsedProductImages());
+ }
+
/**
* @param int $imagesCount
* @param int $batchSize
diff --git a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php
index a4b78287df012..51eba1facb90c 100644
--- a/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php
+++ b/app/code/Magento/MediaStorage/Console/Command/ImagesResizeCommand.php
@@ -8,13 +8,20 @@
namespace Magento\MediaStorage\Console\Command;
use Magento\Framework\App\Area;
+use Magento\Framework\App\ObjectManager;
use Magento\Framework\App\State;
+use Magento\Framework\ObjectManagerInterface;
use Magento\MediaStorage\Service\ImageResize;
+use Symfony\Component\Console\Helper\ProgressBar;
+use Symfony\Component\Console\Helper\ProgressBarFactory;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Helper\ProgressBar;
-use Magento\Framework\ObjectManagerInterface;
+/**
+ * Resizes product images according to theme view definitions.
+ *
+ * @package Magento\MediaStorage\Console\Command
+ */
class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command
{
/**
@@ -28,28 +35,32 @@ class ImagesResizeCommand extends \Symfony\Component\Console\Command\Command
private $appState;
/**
- * @var ObjectManagerInterface
+ * @var ProgressBarFactory
*/
- private $objectManager;
+ private $progressBarFactory;
/**
* @param State $appState
* @param ImageResize $resize
* @param ObjectManagerInterface $objectManager
+ * @param ProgressBarFactory $progressBarFactory
+ * @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function __construct(
State $appState,
ImageResize $resize,
- ObjectManagerInterface $objectManager
+ ObjectManagerInterface $objectManager,
+ ProgressBarFactory $progressBarFactory = null
) {
parent::__construct();
$this->resize = $resize;
$this->appState = $appState;
- $this->objectManager = $objectManager;
+ $this->progressBarFactory = $progressBarFactory
+ ?: ObjectManager::getInstance()->get(ProgressBarFactory::class);
}
/**
- * {@inheritdoc}
+ * @inheritdoc
*/
protected function configure()
{
@@ -58,7 +69,9 @@ protected function configure()
}
/**
- * {@inheritdoc}
+ * @inheritdoc
+ * @param InputInterface $input
+ * @param OutputInterface $output
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
@@ -67,10 +80,12 @@ protected function execute(InputInterface $input, OutputInterface $output)
$generator = $this->resize->resizeFromThemes();
/** @var ProgressBar $progress */
- $progress = $this->objectManager->create(ProgressBar::class, [
- 'output' => $output,
- 'max' => $generator->current()
- ]);
+ $progress = $this->progressBarFactory->create(
+ [
+ 'output' => $output,
+ 'max' => $generator->current()
+ ]
+ );
$progress->setFormat(
"%current%/%max% [%bar%] %percent:3s%% %elapsed% %memory:6s% \t| %message%"
);
@@ -79,9 +94,10 @@ protected function execute(InputInterface $input, OutputInterface $output)
$progress->setOverwrite(false);
}
- for (; $generator->valid(); $generator->next()) {
+ while ($generator->valid()) {
$progress->setMessage($generator->key());
$progress->advance();
+ $generator->next();
}
} catch (\Exception $e) {
$output->writeln("{$e->getMessage()}");
@@ -91,5 +107,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
$output->write(PHP_EOL);
$output->writeln("Product images resized successfully");
+
+ return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
}
}
diff --git a/app/code/Magento/MediaStorage/Service/ImageResize.php b/app/code/Magento/MediaStorage/Service/ImageResize.php
index aae90512b3d95..d3f4fc01e387b 100644
--- a/app/code/Magento/MediaStorage/Service/ImageResize.php
+++ b/app/code/Magento/MediaStorage/Service/ImageResize.php
@@ -152,12 +152,12 @@ public function resizeFromImageName(string $originalImageName)
*/
public function resizeFromThemes(array $themes = null): \Generator
{
- $count = $this->productImage->getCountAllProductImages();
+ $count = $this->productImage->getCountUsedProductImages();
if (!$count) {
throw new NotFoundException(__('Cannot resize images - product images not found'));
}
- $productImages = $this->productImage->getAllProductImages();
+ $productImages = $this->productImage->getUsedProductImages();
$viewImages = $this->getViewImages($themes ?? $this->getThemesInUse());
foreach ($productImages as $image) {
@@ -202,10 +202,12 @@ private function getViewImages(array $themes): array
$viewImages = [];
/** @var \Magento\Theme\Model\Theme $theme */
foreach ($themes as $theme) {
- $config = $this->viewConfig->getViewConfig([
- 'area' => Area::AREA_FRONTEND,
- 'themeModel' => $theme,
- ]);
+ $config = $this->viewConfig->getViewConfig(
+ [
+ 'area' => Area::AREA_FRONTEND,
+ 'themeModel' => $theme,
+ ]
+ );
$images = $config->getMediaEntities('Magento_Catalog', ImageHelper::MEDIA_TYPE_CONFIG_NODE);
foreach ($images as $imageId => $imageData) {
$uniqIndex = $this->getUniqueImageIndex($imageData);
@@ -226,6 +228,7 @@ private function getUniqueImageIndex(array $imageData): string
{
ksort($imageData);
unset($imageData['type']);
+ // phpcs:disable Magento2.Security.InsecureFunction
return md5(json_encode($imageData));
}