Search by

orbis-cms / attachments-images-converter

noith

Converts and compresses Orchid attachment images to a unified format per attachment group.

Package info

gitlab.com/orbis-cms/attachments-images-converter

Issues

pkg:composer/orbis-cms/attachments-images-converter

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 0

dev-master 2026-10-20 01:36 UTC

This package is auto-updated.

Last update: 2026-10-19 22:40:06 UTC


README

Converts Orchid attachment images to a chosen format (webp by default), resizes them to fit per-group bounds without upscaling, generates cropped variants, and optionally keeps the original file. Settings are declared per model and per attachment group.

Requirements

  • PHP with the gd extension (the converter uses Intervention Image's GD driver; WebP output needs a GD build with WebP support).
  • A queue worker. Conversion runs in GenerateImageConversionsJob, queued after the surrounding database transaction commits.

Installation

Publish and run the migration (adds generated_conversions to Orchid's attachments table):

php artisan vendor:publish --tag=attachments-images-migrations
php artisan migrate

How it works

  1. A model that owns attachments declares its group and its conversion settings, side by side, using DeclaresImageConversions:
use OrbisCms\AttachmentsImages\Models\Traits\DeclaresImageConversions;
use OrbisCms\AttachmentsImages\Support\ConversionSettings;
use OrbisCms\AttachmentsImages\Support\ImageFormat;

class Entity extends Model
{
    use DeclaresImageConversions;

    public function imagesAttachmentGroup(): string
    {
        return 'images';
    }

    public static function imagesConversions(): ?ConversionSettings
    {
        return new ConversionSettings(
            main: [1920, 1920],
            sizes: [
                'thumb' => [300, 300],
                'medium' => [800, 600],
            ],
            format: ImageFormat::Webp,
            keepOriginal: false,
        );
    }
}
OptionMeaning
main[maxWidth, maxHeight] of the main image. Images are only ever scaled down; 0 leaves that side unbounded.
sizesNamed variants, each [width, height], cropped to fill exactly (cover).
formatImageFormat::Webp (default), Jpg or Png. Sets the stored extension and mime type.
keepOriginalKeep the uploaded file. It is recorded under the _original key; when the source already has the target format it is copied to <name>@original.<ext>.

Return null (the default) to disable conversion for the model's group.

  1. Register the model once, at boot, in your application's service provider:
use OrbisCms\AttachmentsImages\Registries\AttachmentConversionRegistry;

public function boot(AttachmentConversionRegistry $imageConversions): void
{
    $imageConversions->register(Entity::class);
}

A group can be registered only once: registering a second model with the same imagesAttachmentGroup() throws a LogicException, so give each model its own group.

  1. Your own Attachment model subclass uses HasImageConversions and is registered with Orchid, so it's actually the class that gets instantiated on upload:
namespace App\Models;

use OrbisCms\AttachmentsImages\Models\Traits\HasImageConversions;

class Attachment extends \Orchid\Attachment\Models\Attachment
{
    use HasImageConversions;
}
use Orchid\Platform\Dashboard;

Dashboard::useModel(\Orchid\Attachment\Models\Attachment::class, \App\Models\Attachment::class);

Without this binding no conversion is triggered.

  1. From there it's automatic: uploading an image into a registered group dispatches GenerateImageConversionsJob, which converts/resizes the main file, generates the named sizes, and (optionally) preserves the original. Read a conversion's URL with $attachment->conversionUrl('thumb').

Supported images

JPEG (including image/jpg and image/pjpeg), PNG, WebP and BMP sources are converted. BMP is read but never written. Everything else, GIF and SVG included, is left exactly as uploaded: no conversion, no variants.

Disks

Files are read and written through Laravel's Storage as bytes, so any disk works, including S3. A failed write makes the job throw (ConversionWriteFailedException) even when the disk is not configured with 'throw' => true.

Reading URLs

conversionUrl() does not touch the disk: it returns the recorded URL, or the main file's URL when that conversion was never recorded. Pass throw: true to also verify the file exists and get a ConversionNotFoundException instead of a fallback; on remote disks that costs one request per call.

Failures and retries

If a step fails, the job throws and the source file and the attachment's metadata are left as they were; retry the job. A missing source file throws ConversionSourceMissingException, a failed write ConversionWriteFailedException.

Deleting

Deleting an attachment also deletes its main file and every generated variant (including _original).

Upload deduplication is disabled

The package turns off Orchid's upload deduplication for every upload that goes through Orchid\Attachment\File resolved from the container, including Orchid's own upload endpoint. Each upload gets its own file. Code that builds new File(...) directly keeps Orchid's deduplication.

Converting existing attachments

Attachments that were uploaded before the package was installed are converted with:

php artisan attachments-images:convert-existing

The command converts every attachment in a registered group that has no generated conversions yet, using the same code as a normal upload. It runs synchronously, so no queue worker is needed. The attachment model must use HasImageConversions.

Use --dry-run to see how many attachments would be converted without changing anything.

Backup. Before converting, the command saves:

  • a CSV dump of the selected rows of the attachments table, on the local disk at attachments-images-backup/<timestamp>/attachments.csv;
  • a copy of each original file on the disk it lives on, at attachments-images-backup/<timestamp>/<original path>.

If the backup cannot be written, nothing is converted. The command never deletes the backup. When it finishes it prints the commands that remove the dump and the copies on each disk.

Failures. An attachment that cannot be converted (a corrupt or missing file, or a format the package does not handle, such as GIF or SVG) does not stop the run. The command lists these in a table, saves them to attachments-images-backup/<timestamp>/failures.csv on the local disk and exits with code 1.

Running the command again converts only the attachments that still have no generated conversions.

Config

The only global setting is the encoding quality (default 80). Publish the config file to change it:

php artisan vendor:publish --tag=attachments-images-config