Magento, Magento 2

Magento 2 : Specific Product Image Resize using Command Line

Magento 2 Specific Product Image Resize using Command Line

In this tutorial, Today I will explain to how to specific product image resize using command line in Magento 2. In Magento 2, They’re provide default commad for product image resize. When, we execute command it will resize whole Magento’s product images. But, during development time if you need to check specific product image resize then, how you need to do?

You may also like this :

So, Let’s follow the steps :

1) Let’s assume that you have created simple module. Now, You need to create custom command for that. Create di.xml file at app/code/RH/ImageResize/etc/ and paste the below code for declare command class.

<?xml version="1.0"?>
<!--
/**
 * Created By : Rohan Hapani
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandListInterface">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="imagesResizeCommand" xsi:type="object">
                    RH\ImageResize\Console\Command\ImagesResizeCommand
                </item>
            </argument>
        </arguments>
    </type>
    <type name="RH\ImageResize\Console\Command\ImagesResizeCommand">
        <arguments>
            <argument name="imageResizeScheduler" xsi:type="object">
                Magento\MediaStorage\Service\ImageResizeScheduler\Proxy
            </argument>
        </arguments>
    </type>
</config>

2) Now, You need to create ImagesResizeCommand.php file at app/code/RH/ImageResize/Console/Command/ and paste the below code to add command logic and define custom command :

<?php
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
declare(strict_types=1);

namespace RH\ImageResize\Console\Command;

use Magento\Framework\App\Area;
use Magento\Framework\App\State;
use Magento\Framework\Console\Cli;
use Magento\MediaStorage\Service\ImageResize;
use Magento\MediaStorage\Service\ImageResizeScheduler;
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\Input\InputOption;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Magento\Framework\DB\Query\Generator;
use Magento\Framework\DB\Select;
use Magento\Framework\App\ResourceConnection;
use Magento\Catalog\Model\ResourceModel\Product\Gallery;
use Magento\Catalog\Model\ResourceModel\Product\Image as ProductImage;

/**
 * Resizes product images according to theme view definitions.
 */
class ImagesResizeCommand extends Command
{
    /**
     * Asynchronous image resize mode
     */
    const ASYNC_RESIZE = 'async';

    /**
     * @var ImageResizeScheduler
     */
    private $imageResizeScheduler;

    /**
     * @var ImageResize
     */
    private $imageResize;

    /**
     * @var State
     */
    private $appState;

    /**
     * @var ProgressBarFactory
     */
    private $progressBarFactory;

    /**
     * @var ProductImage
     */
    private $productImage;

    /**
     * @var ResourceConnection
     */
    private $resourceConnection;

    /**
     * @param State $appState
     * @param ImageResize $imageResize
     * @param ImageResizeScheduler $imageResizeScheduler
     * @param ProgressBarFactory $progressBarFactory
     * @param ProductImage $productImage
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function __construct(
        State $appState,
        ImageResize $imageResize,
        ImageResizeScheduler $imageResizeScheduler,
        ProgressBarFactory $progressBarFactory,
        ProductImage $productImage,
        Generator $generator,
        ResourceConnection $resourceConnection
    ) {
        parent::__construct();
        $this->appState = $appState;
        $this->imageResize = $imageResize;
        $this->imageResizeScheduler = $imageResizeScheduler;
        $this->progressBarFactory = $progressBarFactory;
        $this->productImage = $productImage;
        $this->batchQueryGenerator = $generator;
        $this->resourceConnection = $resourceConnection;
    }

    /**
     * @inheritdoc
     */
    protected function configure()
    {
        $this->setName('rh:product:resize')
            ->setDescription('Specific Product Image Resize by Rohan Hapani')
            ->setDefinition($this->getOptionsList());
        $this->addOption(
            'product_id',
            null,
            InputOption::VALUE_REQUIRED,
            'Add Product ID'
        );
    }

    /**
     * Image resize command options list
     *
     * @return array
     */
    private function getOptionsList() : array
    {
        return [
            new InputOption(
                self::ASYNC_RESIZE,
                'a',
                InputOption::VALUE_NONE,
                'Resize image in asynchronous mode'
            ),
        ];
    }

    /**
     * @inheritdoc
     * @param InputInterface $input
     * @param OutputInterface $output
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $product_id = $input->getOption('product_id');

        $result = $this->executeAsync($output, $product_id);
        return $result;
    }

    /**
     * Schedule asynchronous image resizing
     *
     * @param OutputInterface $output
     * @param int $product_id
     * @return int
     */
    private function executeAsync(OutputInterface $output, $product_id): int
    {
        try {
            $errors = [];
            $this->appState->setAreaCode(Area::AREA_GLOBAL);

            /** @var ProgressBar $progress */
            $progress = $this->progressBarFactory->create(
                [
                    'output' => $output,
                    'max' => $this->getCountUsedProductImages($product_id)
                ]
            );
            $progress->setFormat(
                "%current%/%max% [%bar%] %percent:3s%% %elapsed% %memory:6s% \t| <info>%message%</info>"
            );

            if ($output->getVerbosity() !== OutputInterface::VERBOSITY_NORMAL) {
                $progress->setOverwrite(false);
            }

            $productImages = $this->getUsedProductImages($product_id);
            foreach ($productImages as $image) {
                $result = $this->imageResizeScheduler->schedule($image['filepath']);

                if (!$result) {
                    $errors[$image['filepath']] = 'Error image scheduling: ' . $image['filepath'];
                }
                $progress->setMessage($image['filepath']);
                $progress->advance();
            }
        } catch (\Exception $e) {
            $output->writeln("<error>{$e->getMessage()}</error>");
            // we must have an exit code higher than zero to indicate something was wrong
            return Cli::RETURN_FAILURE;
        }

        $output->write(PHP_EOL);
        if (count($errors)) {
            $output->writeln("<info>Product images resized with errors:</info>");
            foreach ($errors as $error) {
                $output->writeln("<error>{$error}</error>");
            }
        } else {
            $output->writeln("<info>Product images scheduled successfully</info>");
        }

        return Cli::RETURN_SUCCESS;
    }

    /**
     * Get used product images.
     *
     * @param int $product_id
     * @return \Generator
     */
    private function getUsedProductImages($product_id): \Generator
    {
        $batchSelectIterator = $this->batchQueryGenerator->generate(
            'value_id',
            $this->getUsedImagesSelect($product_id),
            100,
            \Magento\Framework\DB\Query\BatchIteratorInterface::NON_UNIQUE_FIELD_ITERATOR
        );

        foreach ($batchSelectIterator as $select) {
            foreach ($this->resourceConnection->getConnection()->fetchAll($select) as $key => $value) {
                yield $key => $value;
            }
        }
    }

    /**
     * Return select to fetch all used product images.
     *
     * @param int $product_id
     * @return Select
     */
    private function getUsedImagesSelect($product_id): Select
    {
        
        $query = 'images.disabled = 0 AND image_value.disabled = 0 AND image_value.entity_id = '.$product_id;
        return $this->resourceConnection->getConnection()->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($query);
    }

    /**
     * Get the number of unique and used images of products.
     *
     * @param int $product_id
     * @return int
     */
    private function getCountUsedProductImages($product_id): int
    {
        $select = $this->getUsedImagesSelect($product_id)
            ->reset('columns')
            ->reset('distinct')
            ->columns(
                new \Zend_Db_Expr('count(distinct value)')
            );

        return (int) $this->resourceConnection->getConnection()->fetchOne($select);
    }
}

After deploy this module, You need to execute this below command for resize specific product image.

php bin/magento rh:product:resize --product_id=3

Here, rh:product:resize is custom command and product_id is argument where you need to pass product id. After execute command, You can see output like this :

Image Resize Magento 2

That’s it !!!

I hope this blog is easy to understand about how to specific product image resize using command line in Magento 2. In case, I missed anything or need to add some information, always feel free to leave a comment in this blog, I’ll get back with proper solution.

Keep liking and sharing !!

Tagged ,