How to Extend OpenMage Image Cleaner to Detect Orphans in Custom Module Database Tables

Extend OpenMage Image Cleaner by adding a new sync action in FbimagecleanerController.php that queries your custom table for image paths, compares them against the filesystem using the helper's scandirRecursive() method, and inserts orphaned files into the fb_imagecleaner_image table for admin review.

The OpenMage Image Cleaner extension by Fabrizio Balliano provides a robust framework for identifying unused images in the media/ directory by cross-referencing core Magento tables. While it ships with built-in support for products, categories, and WYSIWYG content, the architecture is intentionally extensible to accommodate custom modules. You can integrate orphan detection for any custom database table by following the established four-step synchronization pattern used by the core actions.

Understanding the Extension Architecture

The cleanup workflow relies on a clear separation between database references and filesystem scans. At its core, the extension compares what is stored in the database against what exists on disk, then persists the difference as orphan records.

Key components in fballiano/openmage-image-cleaner include:

Each sync action follows an identical pattern: determine an entity type ID, fetch image paths from relevant database tables, scan the corresponding media subdirectory, compute the difference, and write orphan entries to the cleaner table.

Step-by-Step Implementation Guide

Choose a Unique Entity Type ID

Select a positive integer that does not conflict with existing core IDs (category = 3, product = 4). For a custom module, you can hard-code this value or make it configurable via system.xml.

<!-- app/code/local/YourNamespace/YourModule/etc/config.xml -->
<default>
    <admin>
        <custom_image_cleaner>
            <entity_type_id>5</entity_type_id>
        </custom_image_cleaner>
    </admin>
</default>

Retrieve it in your controller using:

$entity_type_id = (int)Mage::getStoreConfig('admin/custom_image_cleaner/entity_type_id');

Create the Custom Sync Action

Add a new method to FbimagecleanerController.php that mirrors the logic of syncproductAction. This method queries your custom table, normalizes the paths, and populates the orphan table.

public function syncCustomAction()
{
    $entity_type_id = (int)Mage::getStoreConfig('admin/custom_image_cleaner/entity_type_id');
    $media_dir      = Mage::getBaseDir('media') . '/custom';
    $resource       = Mage::getSingleton('core/resource');
    $db             = $resource->getConnection('core_read');
    $helper         = Mage::helper('fballiano_imagecleaner');

    if (!is_dir($media_dir)) {
        Mage::getSingleton('adminhtml/session')
            ->addError($this->__('"media/custom" folder does not exist.'));
        $this->_redirect('*/*');
        return;
    }

    // 1️⃣ Fetch image references from custom DB table
    $db_images = $db->fetchCol(
        "SELECT file_path FROM {$resource->getTableName('my_module_image')} WHERE file_path IS NOT NULL"
    );
    $db_images = array_map('ltrim', $db_images, ['/']);

    // 2️⃣ Scan the filesystem recursively
    $fs_images = $helper->scandirRecursive($media_dir);
    $fs_images = str_replace("$media_dir/", '', $fs_images);

    // 3️⃣ Compute orphans (files on disk not in DB)
    $unused_images = array_diff($fs_images, $db_images);

    // 4️⃣ Insert new orphans into fb_imagecleaner_image
    if ($unused_images) {
        $cleaner_table = $resource->getTableName('fb_imagecleaner_image');
        $already_seen  = $db->fetchCol(
            "SELECT path FROM {$cleaner_table} WHERE entity_type_id = {$entity_type_id}"
        );
        $unused_images = array_diff($unused_images, $already_seen);

        foreach ($unused_images as $path) {
            $db->insertIgnore($cleaner_table, [
                'entity_type_id' => $entity_type_id,
                'path'           => $path
            ]);
        }
    }

    $this->_redirect('*/*');
}

Replace my_module_image and file_path with your actual table and column names. The insertIgnore method prevents duplicate entries if the same orphan is detected across multiple sync operations.

Register the Admin Action (Optional)

To expose a button in the admin UI, extend system.xml to add a new configuration field or button that routes to */*/syncCustom. Alternatively, you can trigger the action manually via URL:


https://yourstore.com/index.php/admin/fbimagecleaner/syncCustom

Verify Orphan Detection Results

After running the sync action, navigate to System → Tools → Image Cleaner in the admin panel. Orphaned images from your custom module will appear in the grid with the corresponding entity type ID, allowing you to review and delete them individually or in bulk using the existing interface.

Summary

  • Reuse the existing controller pattern in FbimagecleanerController.php to maintain consistency with core sync actions.
  • Assign a unique entity type ID to differentiate custom module images from core entities in the fb_imagecleaner_image table.
  • Leverage the helper's scandirRecursive() method to reliably scan media subdirectories without writing custom filesystem logic.
  • Normalize database paths by stripping leading slashes to ensure accurate comparison with filesystem results.
  • No UI modifications required — the admin grid automatically displays any rows inserted into the cleaner table.

Frequently Asked Questions

What database table stores the detected orphan images?

The extension uses the fb_imagecleaner_image table, mapped by Fballiano_ImageCleaner_Model_Resource_Image. When you insert records with a specific entity_type_id and path, they immediately appear in the Image Cleaner admin grid without requiring additional code changes.

Can I use the same entity type ID for multiple custom modules?

No, you should assign a unique entity type ID to each distinct image source. The ID acts as a filter in the admin grid and determines which media subdirectory the scanner targets. Reusing IDs would cause collisions in the orphan list and incorrect association of images.

Does the extension automatically delete files from the custom module's folder?

No, the sync action only detects and records orphaned files. The actual deletion occurs through the admin UI when an administrator selects records and chooses the delete action. This two-step process prevents accidental removal of potentially critical files.

How do I handle multiple image columns in a single custom table?

Extend the database query in your sync action to fetch all relevant columns, then merge the results into a single flat array before comparing against the filesystem. For example, if your table has thumbnail and main_image columns, perform two fetchCol calls and use array_merge() to create a comprehensive list of referenced paths.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →