Skip to content

Instantly share code, notes, and snippets.

@perryholden
Last active October 11, 2023 15:10
Show Gist options
  • Save perryholden/54bf085b983a9a423b50c3a047b63e08 to your computer and use it in GitHub Desktop.
Save perryholden/54bf085b983a9a423b50c3a047b63e08 to your computer and use it in GitHub Desktop.
Finds orphaned files from deleted products in Magento 2 (partially generated with ChatGPT v4)
<?php
use Magento\Framework\App\Bootstrap;
require __DIR__ . '/app/bootstrap.php';
/*
* There are a few instances where a file exists in the EAV tables, yet not in the media table, therefore, we should just keep these.
* These were generated by the following SQL (please update attribute IDs, if needed):
SELECT `value` AS 'image'
FROM catalog_product_entity_varchar
WHERE store_id = 0
AND attribute_id = 87
AND `value` IS NOT NULL AND `value` NOT IN (
(SELECT m2.value
FROM catalog_product_entity_media_gallery_value m1
INNER JOIN catalog_product_entity_media_gallery m2 ON m2.value_id = m1.value_id
AND m2.attribute_id = 90
GROUP BY m1.entity_id)
) AND `value` <> 'no_selection'
*/
$filesToKeep = [
'/0/1/0123.jpg',
'/0/2/0234.jpg',
];
$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get('Magento\Framework\App\State');
$state->setAreaCode('frontend');
// Filesystem and CSV dependencies
$fileSystem = $objectManager->create('\Magento\Framework\Filesystem');
$mediaDirectory = $fileSystem->getDirectoryRead(\Magento\Framework\App\Filesystem\DirectoryList::MEDIA);
$productMediaConfig = $objectManager->create('Magento\Catalog\Model\Product\Media\Config');
$csvProcessor = $objectManager->create('Magento\Framework\File\Csv');
$mediaGalleryResourceModel = $objectManager->create('Magento\Catalog\Model\ResourceModel\Product\Gallery');
// Fetch all images from the database
$connection = $mediaGalleryResourceModel->getConnection();
$select = $connection->select()->from($mediaGalleryResourceModel->getMainTable(), 'value');
$allDbImages = $connection->fetchCol($select);
// Path to the directory where product images are stored
$mediaGalleryPath = $productMediaConfig->getBaseMediaPath();
$orphanedImages = [];
$allFiles = $mediaDirectory->readRecursively($mediaGalleryPath);
$totalFileSize = 0;
foreach ($allFiles as $file) {
$relativeFilePath = str_replace("catalog/product", '', $file);
if (
is_file(BP . '/pub/media/' . $file) &&
!in_array($relativeFilePath, $allDbImages) &&
!in_array($relativeFilePath, $filesToKeep) &&
(!str_starts_with($relativeFilePath, '/cache/')) &&
(!str_starts_with($relativeFilePath, '/placeholder/'))
) {
if ($orphanedFileSize = filesize(BP . '/pub/media/' . $file)) {
$totalFileSize += $orphanedFileSize;
}
$orphanedImages[] = [$relativeFilePath];
}
}
// If orphaned images are found, save to CSV
if (!empty($orphanedImages)) {
$csvFile = 'orphaned_images.csv';
$csvProcessor->saveData($csvFile, $orphanedImages);
echo "Orphaned images saved to $csvFile\n";
$approxSpace = human_filesize($totalFileSize);
echo "Approximate total space to recover: $approxSpace\n";
} else {
echo "No orphaned images found.\n";
}
function human_filesize($bytes, $decimals = 2) {
$sz = 'BKMGTP';
$factor = floor((strlen($bytes) - 1) / 3);
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment