Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Aio Images Laravel Package

mr-timofey/laravel-aio-images

A Laravel package for all-in-one image handling. It aims to simplify common media workflows such as uploads, storage, and transformations through a unified API, helping reduce boilerplate when adding image features to Laravel applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and confirm what you're actually getting. The package is MIT-licensed but small and unmaintained-looking (3 stars, 0 dependents). Before wiring it into anything, read the source — it's almost certainly a thin wrapper and small enough to audit in 15 minutes:

composer require mr-timofey/laravel-aio-images

Laravel will auto-discover the service provider via extra.laravel.providers in the package's composer.json. For Lumen you must register it manually in bootstrap/app.php and copy the config yourself — Lumen has no package discovery and no vendor:publish helper by default:

$app->register(MrTimofey\AioImages\AioImagesServiceProvider::class);
$app->configure('aio-images');

Where to look first, in order:

  1. vendor/mr-timofey/laravel-aio-images/config/ — the preset/size definitions live here; this is the package's real API surface.
  2. composer.json of the package — check which intervention/image major version it pins. That single line determines whether you're on the v2 (->resize(), ->save()) or v3 (ImageManager, ->scale()) API, and it will constrain your whole app.
  3. The service provider's register()/boot() — look for what it binds into the container and whether it publishes assets, registers a disk, or overrides anything globally.
php artisan vendor:publish --provider="MrTimofey\AioImages\AioImagesServiceProvider" --tag=config

First use case: the one thing this package earns its keep on is declarative size presets. Instead of scattering Image::make($file)->resize(300, 200) through controllers, you define named variants once ("thumb", "card", "hero", "og") and request them by name at the call site. Start there:

  • Define 2–3 presets in config/aio-images.php with explicit widths/heights and a crop/fit strategy.
  • Point the package at a storage disk (public or an S3 disk), not bare filesystem paths — this is the difference between code that survives a deployment and code that doesn't.
  • Process one upload end-to-end in a test route before touching your real upload flow. If the package saves to storage/app/public/..., verify php artisan storage:link has been run.

If the config file doesn't expose presets, or the README is a stub, treat the package as a reference implementation rather than a dependency — the presets idea is worth copying, but the maintenance risk isn't worth inheriting.

Implementation Patterns

Preset-driven variant generation. The core pattern: define variants in config, loop over them at upload time, and persist the resulting paths as a JSON column on your model rather than recomputing them.

// config/aio-images.php
'presets' => [
    'thumb' => ['width' => 150, 'height' => 150, 'fit' => 'crop'],
    'card'  => ['width' => 600, 'height' => 400, 'fit' => 'crop'],
    'hero'  => ['width' => 1600, 'height' => null, 'fit' => 'width'],
],
// app/Actions/StoreImage.php
public function handle(UploadedFile $file, string $collection = 'default'): array
{
    $paths = [];
    foreach (config('aio-images.presets') as $name => $preset) {
        $paths[$name] = $this->processor->process($file, $preset, "{$collection}/{$name}");
    }
    return $paths;
}

Store $paths on the model ($casts = ['images' => 'array']) so a change to a preset name doesn't silently break old rows — you can detect and backfill.

Dispatch to a queue. Image decoding is CPU- and memory-bound. Never do it inside the HTTP request for anything above ~2MB. Push a job after the upload, store the original immediately, and have the job generate the variants and update the model. This also makes retries safe if the process OOMs.

GenerateImageVariants::dispatch($model)->onQueue('images');

Give that queue its own worker with a higher memory_limit than your web workers (--memory=512), because image processing routinely exceeds PHP's 128M default.

On-demand resizing with a cache layer. For user-supplied dimensions (avatars, arbitrary crops), don't pre-generate — generate on first request keyed by a hash of the source path + dimensions, and serve from cache thereafter. The package's processor can be called from a controller route:

Route::get('/img/{path}', ImageController::class)->where('path', '.*');

Guard that route hard: validate the path resolves inside your configured source directory, reject .. traversal, and allowlist which dimensions are accepted. An open image-resizing endpoint is a free CPU DoS.

Blade integration. Wrap variant lookup in a small accessor or component so templates never touch the processor:

<x-image :model="$post" preset="card" />

The component reads $post->images['card'], falls back to the original if missing, and emits srcset across presets. Keeping the fallback in one place matters — half your rows will be missing variants the day you add a new preset.

Model observer for cleanup. When an image-bearing model is deleted, delete the directory, not just the DB row. With an S3 disk this is a real cost issue; with local storage it's a disk-fill issue.

Gotchas and Tips

This is a low-traffic package — pin it exactly. "mr-timofey/laravel-aio-images": "1.0.*" rather than ^1.0, and commit composer.lock. With 3 stars and 0 dependents, there is no community pressure to keep the API stable, and a minor bump can rename config keys with no changelog. Budget for the possibility that you fork it.

Driver divergence is the #1 source of "it works locally." GD and Imagick produce different results for the same crop, handle EXIF rotation differently, and support different formats. Set the driver explicitly in config rather than inheriting whatever intervention/image picks up, and make sure your Docker image and your local machine agree. GIF (animated) and WebP are the usual casualties — verify both.

Memory limits, concretely. A 4000×3000 JPEG decodes to roughly 48MB as a GD resource before any resizing happens, and each preset iteration may hold another copy. If you loop presets in one request, freed resources must actually be freed — check that the package's processor doesn't accumulate. If it does, run one preset per job.

Config path vs. storage disk confusion. Packages that take a filesystem path (/var/www/storage/app/public) break the moment you move to S3 or a multi-server setup. If the config only accepts paths, wrap the package behind your own service that downloads to a temp file, processes, and re-uploads — and keep that wrapper even on local storage so the swap is one-class change.

Cache invalidation on preset change. Changing card from 600×400 to 600×450 does not regenerate existing files. Version your output paths (card-v2/...) or add a preset_hash column so you can find and rebuild stale variants. Silent staleness is easy to miss because the images still render — just at the wrong size.

EXIF orientation. Phone uploads are frequently stored landscape with an orientation flag. If the package doesn't call ->orientate() (Intervention v3: ->orient()), portraits will come out sideways. Check this with an actual iPhone photo, not a synthetic test image.

Intervention major-version lock-in. If the package pins intervention/image ^2.0, you cannot use v3 anywhere else in the app — the Image facade and ImageManager are different classes. If you're already on v3, this alone may disqualify the package; verify before installing, not after.

storage:link and public URLs. Variants written to storage/app/public are invisible until symlinked, and the symlink must be recreated on each deploy (include it in your deploy script — php artisan storage:link is idempotent).

Debugging. When a variant is missing, check in this order: (1) does the original exist at the path the config expects, (2) is the directory writable by the PHP-FPM/queue user — not just your shell user, (3) did the queue job fail silently (failed_jobs table), (4) is the disk's url config producing a double slash that renders as a 404. The first and fourth catch most reports.

Extension points to lean on. The processor is almost certainly bound as a singleton in the container — that's your seam. If you need EXIF stripping, watermarking, or a different naming scheme, bind your own subclass via $this->app->bind(...) in a AppServiceProvider rather than editing vendor files. That's also your exit ramp if you later replace the package: everything funnels through one class you control.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource