diff --git a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php index 123f358be40c8..77f67480619e0 100644 --- a/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php +++ b/app/code/Magento/Catalog/Model/ResourceModel/Product/Image.php @@ -12,6 +12,9 @@ use Magento\Framework\DB\Select; use Magento\Framework\App\ResourceConnection; +/** + * Class for retrieval of all product images + */ class Image { /** @@ -73,15 +76,24 @@ public function getAllProductImages(): \Generator /** * Get the number of unique pictures of products + * * @return int */ public function getCountAllProductImages(): int { - $select = $this->getVisibleImagesSelect()->reset('columns')->columns('count(*)'); + $select = $this->getVisibleImagesSelect() + ->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 */ private function getVisibleImagesSelect(): Select 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 new file mode 100644 index 0000000000000..4fce12dc2de89 --- /dev/null +++ b/app/code/Magento/Catalog/Test/Unit/Model/ResourceModel/Product/ImageTest.php @@ -0,0 +1,237 @@ +objectManager = + new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->connectionMock = $this->createMock(AdapterInterface::class); + $this->resourceMock = $this->createMock(ResourceConnection::class); + $this->resourceMock->method('getConnection') + ->willReturn($this->connectionMock); + $this->resourceMock->method('getTableName') + ->willReturnArgument(0); + $this->generatorMock = $this->createMock(Generator::class); + } + + /** + * @return MockObject + */ + protected function getVisibleImagesSelectMock(): 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('where') + ->with('disabled = 0') + ->willReturnSelf(); + + return $selectMock; + } + + /** + * @param int $imagesCount + * @dataProvider dataProvider + */ + public function testGetCountAllProductImages(int $imagesCount): void + { + $selectMock = $this->getVisibleImagesSelectMock(); + $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->getCountAllProductImages() + ); + } + + /** + * @param int $imagesCount + * @param int $batchSize + * @dataProvider dataProvider + */ + public function testGetAllProductImages( + int $imagesCount, + int $batchSize + ): void { + $this->connectionMock->expects($this->once()) + ->method('select') + ->willReturn($this->getVisibleImagesSelectMock()); + + $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) + ) + ); + + $imageModel = $this->objectManager->getObject( + Image::class, + [ + 'generator' => $this->generatorMock, + 'resourceConnection' => $this->resourceMock, + 'batchSize' => $batchSize + ] + ); + + $this->assertCount($imagesCount, $imageModel->getAllProductImages()); + } + + /** + * @param int $imagesCount + * @param int $batchSize + * @return \Closure + */ + protected function getFetchResultCallbackForBatches( + int $imagesCount, + int $batchSize + ): \Closure { + $fetchResultsCallback = function () use (&$imagesCount, $batchSize) { + $batchSize = + ($imagesCount >= $batchSize) ? $batchSize : $imagesCount; + $imagesCount -= $batchSize; + + $getFetchResults = function ($batchSize): array { + $result = []; + $count = $batchSize; + while ($count) { + $count--; + $result[$count] = $count; + } + + return $result; + }; + + return $getFetchResults($batchSize); + }; + + return $fetchResultsCallback; + } + + /** + * @param Select | MockObject $selectMock + * @param int $batchCount + * @return \Closure + */ + protected function getBatchIteratorCallback( + MockObject $selectMock, + int $batchCount + ): \Closure { + $iteratorCallback = function () use ($batchCount, $selectMock): array { + $result = []; + $count = $batchCount; + while ($count) { + $count--; + $result[$count] = $selectMock; + } + + return $result; + }; + + return $iteratorCallback; + } + + /** + * Data Provider + * @return array + */ + public function dataProvider(): array + { + return [ + [300, 300], + [300, 100], + [139, 100], + [67, 10], + [154, 47], + [0, 100] + ]; + } +}