Magento stores information about product image in 2 places
In Gallery the list of available images for product with properties
In catalog_product_entity_varchar
the value selected for image type like thumbnail
. Empty value should be no_selection
Magento uses data provider in UI listing and the value for grid comes from 2nd place
You need to reset all image types for product before save in your remove logic like
$product->setData('image', 'no_selection');
$product->setData('small_image','no_selection');
// $product->setData('swatch_image', 'no_selection');
$product->setData('thumbnail', 'no_selection');
// or you can use method getMediaAttributes for get all media attributes
foreach ($product->getMediaAttributes() as $attribute) {
$product->setData($attribute->getAttributeCode(), 'no_selection');
}
also you can use logic from \Magento\Catalog\Model\Product\Gallery\CreateHandler
// this logic persist value in DB
$product->addAttributeUpdate(
$mediaAttrCode,
$newValue,
$storeId
);
// receive raw value from DB
// \Magento\Catalog\Model\ResourceModel\Product
$thumbnail = $productResource->getAttributeRawValue($product->getId(),
'thumbnail', $product->getStore());
[Update]
Helper to remove all product images. Just move to your namespace and try
<?php
declare(strict_types=1);
namespace Acme\StackExchange\Helper;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Gallery\Processor as ProcessorAlias;
use Magento\Catalog\Model\Product\Media\ConfigInterface as MediaConfigInterface;
use Magento\Catalog\Model\ProductRepository;
use Magento\Catalog\Model\ResourceModel\Product\Gallery as GalleryResource;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\Filesystem;
use Psr\Log\LoggerInterface;
class CatalogProduct extends AbstractHelper
{
protected ProductRepository $productRepository;
protected GalleryResource $galleryResource;
protected ProcessorAlias $galleryProcessor;
protected MediaConfigInterface $mediaConfig;
protected Filesystem $filesystem;
protected LoggerInterface $logger;
public function __construct(
Context $context,
ProductRepository $productRepository,
GalleryResource $galleryResource,
ProcessorAlias $galleryProcessor,
MediaConfigInterface $mediaConfig,
Filesystem $filesystem,
LoggerInterface $logger
) {
parent::__construct($context);
$this->productRepository = $productRepository;
$this->galleryResource = $galleryResource;
$this->galleryProcessor = $galleryProcessor;
$this->mediaConfig = $mediaConfig;
$this->filesystem = $filesystem;
$this->logger = $logger;
}
public function deleteAllImages(int $productId, bool $physical = false)
{
/** @var Product $product */
$product = $this->productRepository->getById($productId, true, 0);
$valueIds = [];
$removeFiles = [];
foreach ($product->getMediaGalleryImages() as $galleryImage) {
$valueIds[] = $galleryImage['value_id'];
$removeFiles[] = $galleryImage['file'];
}
if ($valueIds) {
$this->galleryResource->deleteGallery($valueIds);
}
foreach ($product->getMediaAttributes() as $attribute) {
$value = $product->getData($attribute->getAttributeCode());
// remove non-gallery image file
if ($value && $value !== 'no_selection') {
if (!in_array($value, $removeFiles, true)) {
$removeFiles[] = $value;
}
}
$product->setData($attribute->getAttributeCode(), 'no_selection');
}
if ($physical) {
$mediaDir = $this->filesystem->getDirectoryWrite(DirectoryList::MEDIA);
$mediaPath = $this->mediaConfig->getBaseMediaPath();
foreach ($removeFiles as $removeFile) {
if (!$this->galleryResource->countImageUses($removeFile)) {
$filePath = $mediaPath . $removeFile;
if ($mediaDir->isFile($filePath)) {
try {
$this->galleryProcessor->removeImage($product, $removeFile);
$mediaDir->delete($filePath);
} catch (\Exception $e) {
$this->logger->critical($e);
}
}
}
}
}
$product->unsetData('media_gallery');
$this->productRepository->save($product);
}
}
Using
// remove only values
$helper->deleteAllImages(1884);
// remove values and files
$helper->deleteAllImages(1884, true);