- You need to add your logic to
catalog_product_save_commit_after
event
app/code/Acme/StackExchange/etc/events.xml
<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="catalog_product_save_commit_after">
<observer name="acme_stackexchange_observer_product_save_commit_after_media"
instance="Acme\StackExchange\Observer\Catalog\NormalizeProductMedia"/>
</event>
</config>
- Observer to update image names and labels
app/code/Acme/StackExchange/Observer/Catalog/NormalizeProductMedia.php
<?php
declare(strict_types=1);
namespace Acme\StackExchange\Observer\Catalog;
use Acme\StackExchange\Model\ResourceModel\Catalog\Product\Gallery as GalleryResource;
use Magento\Catalog\Api\Data\ProductAttributeInterface;
use Magento\Catalog\Api\Data\ProductInterface;
use Magento\Catalog\Api\ProductAttributeRepositoryInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\Product\Media\Config as MediaConfig;
use Magento\Framework\App\Filesystem\DirectoryList;
use Magento\Framework\EntityManager\EntityMetadata;
use Magento\Framework\EntityManager\EntityMetadataInterface;
use Magento\Framework\EntityManager\MetadataPool;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Exception\FileSystemException;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\Filesystem;
use Magento\Framework\Filesystem\Directory\WriteInterface;
use Magento\MediaStorage\Helper\File\Storage\Database as DatabaseStorage;
use Magento\MediaStorage\Model\File\Uploader as FileUploader;
class NormalizeProductMedia implements ObserverInterface
{
/**
* @var EntityMetadata|EntityMetadataInterface
*/
protected $metadata;
/**
* @var MediaConfig
*/
protected $mediaConfig;
/**
* @var GalleryResource
*/
protected $galleryResource;
/**
* @var ProductAttributeInterface
*/
protected $attribute;
/**
* @var WriteInterface
*/
protected $mediaDirectory;
/**
* @var WriteInterface
*/
protected $pubDirectory;
/**
* @var DatabaseStorage
*/
protected $fileStorageDb;
/**
* @param MetadataPool $metadataPool
* @param MediaConfig $mediaConfig
* @param GalleryResource $galleryResource
* @param ProductAttributeRepositoryInterface $attributeRepository
* @param Filesystem $filesystem
* @param DatabaseStorage $fileStorageDb
* @throws FileSystemException
* @throws NoSuchEntityException
*/
public function __construct(
MetadataPool $metadataPool,
MediaConfig $mediaConfig,
GalleryResource $galleryResource,
ProductAttributeRepositoryInterface $attributeRepository,
Filesystem $filesystem,
DatabaseStorage $fileStorageDb
) {
$this->metadata = $metadataPool
->getMetadata(ProductInterface::class);
$this->mediaConfig = $mediaConfig;
$this->attribute = $attributeRepository->get('media_gallery');
$this->galleryResource = $galleryResource;
$this->mediaDirectory = $filesystem
->getDirectoryWrite(DirectoryList::MEDIA);
$this->pubDirectory = $filesystem
->getDirectoryWrite(DirectoryList::PUB);
$this->fileStorageDb = $fileStorageDb;
}
/**
* @inheritDoc
*/
public function execute(Observer $observer): void
{
/** @var Product $product */
$product = $observer->getEvent()->getData('product');
$images = $this->galleryResource->loadProductGalleryByAttributeId(
$product, $this->attribute->getAttributeId());
if (!$images) {
return;
}
foreach ($images as &$image) {
if ($image['media_type'] !== 'image') {
continue;
}
$this->updateImageLabel($product, $image);
if (!$this->fileStorageDb->checkDbUsage()) {
$this->updateImageFilename($product, $image);
}
}
unset($image);
}
/**
* @param Product $product
* @param array $image
*/
protected function updateImageLabel(Product $product, array &$image): void
{
$isNameChanged = $product->dataHasChangedFor('name');
if ((string)$image['label'] !== '' && !$isNameChanged) {
return;
}
if ($isNameChanged) {
$oldName = $product->getOrigData('name');
// keep custom label
if (stripos((string)$image['label'], $oldName . ' - ') === false) {
return;
}
}
$image['label'] = sprintf('%s - %d',
$product->getName(), $image['position'] + 1);
$storeId = (int)$product->getStoreId();
$linkField = $this->metadata->getLinkField();
$data = [
'value_id' => $image['value_id'],
'label' => $image['label'],
'position' => isset($image['position']) ? (int)$image['position'] : 0,
'disabled' => isset($image['disabled']) ? (int)$image['disabled'] : 0,
'store_id' => $storeId,
$linkField => (int)$product->getData($linkField),
];
try {
$this->galleryResource->updateGalleryValue($data);
$hasLabel = ['image', 'small_image', 'thumbnail'];
$affected = $this->getAffectedMediaAttributes($product, $image);
foreach ($affected as $attrCode) {
if (in_array($attrCode, $hasLabel, true)) {
$product->setData($attrCode . '_label', $image['label']);
$product->addAttributeUpdate(
$attrCode . '_label',
$image['label'],
$storeId
);
}
}
} catch (\Exception $e) {
}
}
/**
* @param Product $product
* @param array $image
*/
protected function updateImageFilename(
Product $product,
array &$image
): void {
$file = $image['file'];
if (empty($file)) {
return;
}
if (strpos($file, $product->getSku() . '_') !== false) {
return;
}
$newFile = false;
$oldFile = $file;
if (!$this->mediaDirectory->isExist($this->mediaConfig->getMediaPath($oldFile))) {
return;
}
$affected = $this->getAffectedMediaAttributes($product, $image);
$fileExt = strtolower(pathinfo($oldFile, PATHINFO_EXTENSION));
$fileExt = strtr($fileExt, ['jpeg' => 'jpg']);
try {
for ($i = 1; $i < 500; $i++) {
$tmpName = sprintf('%s_slide%d.%s',
$product->getSku(), $i, $fileExt);
$tmpPath = FileUploader::getDispersionPath($tmpName);
$tmpFile = $this->mediaConfig->getMediaPath($tmpPath . '/' . $tmpName);
if (!$this->mediaDirectory->isExist($tmpFile)) {
$newFile = $tmpPath . '/' . $tmpName;
break;
}
}
if (!$newFile) {
return;
}
$this->mediaDirectory->renameFile(
$this->mediaConfig->getMediaPath($oldFile),
$this->mediaConfig->getMediaPath($newFile)
);
$this->clearProductCache($newFile);
$image['file'] = $newFile;
$this->saveGalleryEntity($image);
$this->saveAffectedAttributes($product, $affected, $newFile);
} catch (\Exception $e) {
}
}
/**
* @param Product $product
* @param array $image
* @return array
*/
protected function getAffectedMediaAttributes(
Product $product,
array $image
): array {
$file = $image['new_file'] ?? $image['file'];
if (empty($file)) {
return [];
}
$affected = [];
$attributes = $this->mediaConfig->getMediaAttributeCodes();
foreach ($attributes as $attrCode) {
if ($product->getData($attrCode) === $file) {
$affected[] = $attrCode;
}
}
return $affected;
}
/**
* @param Product $product
* @param array $affected
* @param string $file
*/
protected function saveAffectedAttributes(
Product $product,
array $affected,
string $file
): void {
foreach ($affected as $attrCode) {
$product->setData($attrCode, $file);
$product->addAttributeUpdate(
$attrCode,
$file,
(int)$product->getStoreId()
);
}
}
/**
* @param array $image
*/
protected function saveGalleryEntity(array $image): void
{
$data = [
'value_id' => $image['value_id'],
'attribute_id' => (int)$this->attribute->getAttributeId(),
'value' => $image['file'],
'media_type' => $image['media_type'],
'disabled' => $image['disabled'] ?? 0,
];
$this->galleryResource->saveDataRow(
$this->galleryResource::GALLERY_TABLE,
$data,
['value']
);
}
/**
* @param string $file
*/
protected function clearProductCache(string $file): void
{
try {
$pattern = sprintf('cache/*/%s', ltrim($file, '/'));
$path = $this->mediaConfig->getBaseMediaPath();
$result = $this->mediaDirectory->search($pattern, $path);
foreach ($result as $cacheFile) {
$this->mediaDirectory->delete($cacheFile);
}
} catch (\Exception $e) {
}
}
}
- Some extend to Gallery Resource
app/code/Acme/StackExchange/Model/ResourceModel/Catalog/Product/Gallery.php
<?php
declare(strict_types=1);
namespace Acme\StackExchange\Model\ResourceModel\Catalog\Product;
use Magento\Framework\DataObject;
use Magento\Framework\Exception\LocalizedException;
class Gallery extends \Magento\Catalog\Model\ResourceModel\Product\Gallery
{
public function updateGalleryValue(array $data)
{
$connection = $this->getConnection();
$linkField = $this->metadata->getLinkField();
if (!isset($data['value_id'], $data[$linkField], $data['store_id'])) {
throw new LocalizedException(__('Missed required fields for Media Gallery Value'));
}
unset($data['record_id']);
$data = $this->_prepareDataForTable(
new DataObject($data),
$this->getTable(self::GALLERY_VALUE_TABLE)
);
$select = $connection->select()
->from($this->getTable(self::GALLERY_VALUE_TABLE), 'record_id')
->where('value_id = ?', (int)$data['value_id'])
->where($linkField . ' = ?', (int)$data[$linkField])
->where('store_id = ?', (int)$data['store_id']);
$recordId = $connection->fetchOne($select);
if ($recordId !== false) {
$connection->update(
$this->getTable(self::GALLERY_VALUE_TABLE),
$data,
$connection->quoteInto('record_id = ?', (int)$recordId)
);
} else {
$connection->insert(
$this->getTable(self::GALLERY_VALUE_TABLE),
$data
);
}
return $this;
}
}
Feel free to update a logic inside according to you requirements