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

Symfony Intervention Image Bundle Laravel Package

janwebdev/symfony-intervention-image-bundle

Symfony bundle that wires Intervention Image into your application, giving you a service-based way to resize, crop, and manipulate images through Symfony's container and configuration. A lightweight integration for projects that need image processing without manual setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: it is a Symfony bundle, not a Laravel package. The name is literal — it ships a Bundle subclass, a DI extension, and a config tree, and it exists to wire Intervention\Image\ImageManager into the Symfony container. Laravel's package discovery only looks for Illuminate\Support\ServiceProvider subclasses, so composer require janwebdev/symfony-intervention-image-bundle in a Laravel app installs the code and registers nothing. You also drag in symfony/framework-bundle, symfony/dependency-injection, and symfony/config for zero benefit. A new tag does not change that: v1.0.0 is still a bundle.

So the correct "getting started" is:

# Do NOT install the bundle in Laravel. Install the library.
composer require intervention/image

# Or, for a Laravel-native binding + facade:
composer require intervention/image-laravel

What the v1.0.0 tag changes (and what it doesn't). The release notes are a single line — "initial release" — and no changelog, diff summary, or dependency metadata accompanied it. That is a version reset on a package whose previous line dated to mid-2022, not a documented feature release. Concretely, it means one prior claim in this assessment is now stale: you can no longer say "the last release was 2022, so it targets Intervention 2.x" without checking. Before you reason about it at all, run:

composer show janwebdev/symfony-intervention-image-bundle --all
# then read the constraints directly
composer show -a janwebdev/symfony-intervention-image-bundle | grep -E 'intervention|symfony|php'

Two possible worlds, and the release note does not tell you which:

  • Constraint modernised (intervention/image:^3, PHP 8.1+, Symfony 6/7) → the bundle is a credible wiring layer for a Symfony app again, but it is still irrelevant to Laravel.
  • Constraint unchanged → v1.0.0 is a tag bump with a misleading message, and ^2.7 still hard-blocks Intervention 3.

Where to look first, depending on why you're here:

  • You want image manipulation in Laravel → look at intervention/image docs, section "Installation" and "Reading images". The bundle adds nothing you can't write in 15 lines, in either world.
  • You're supporting a legacy Symfony app that already has this bundle → look in vendor/janwebdev/symfony-intervention-image-bundle/DependencyInjection/ for Configuration.php and *Extension.php to get the exact config keys (driver option, likely gd vs imagick) and the ImageManager service ID. Then confirm the installed Intervention major from composer show intervention/image, because the 2.x→3.x API break is what determines every line of code you write.

Minimal Laravel equivalent of what the bundle does:

// app/Providers/AppServiceProvider.php
use Intervention\Image\ImageManager;
use Intervention\Image\Drivers\Gd\Driver;

public function register(): void
{
    $this->app->singleton(ImageManager::class, fn () => new ImageManager(
        new Driver(), // or new ImagickDriver()
    ));
}

First use case: thumbnail generation for user uploads. Upload the original untouched, dispatch a queued job that reads the file, produces 2–3 widths, and writes WebP variants. That is 90% of real-world usage and the only scenario worth wiring up on day one.

Implementation Patterns

Inject the manager, never the static facade. In Intervention v3 the static Image::make() API from 2.x is gone; $manager->read() is the entry point and it is trivially testable. Injecting ImageManager into jobs and action classes keeps your controllers clean and lets you swap the driver in tests. If v1.0.0 turns out to be a v3-era bundle, this is the only integration style it can support anyway.

Queued variant generation (the pattern you'll actually reuse):

final class GenerateImageVariants implements ShouldQueue
{
    public function __construct(
        public string $disk,
        public string $path,
        public array $widths = [160, 480, 960],
    ) {}

    public function handle(ImageManager $images): void
    {
        $storage = Storage::disk($this->disk);
        $image = $images->read($storage->path($this->path))
            ->orientate(); // fix EXIF rotation once, up front

        foreach ($this->widths as $width) {
            $encoded = $image->scale(width: $width)->toWebp(80);

            $storage->put(
                "variants/{$width}/{$this->path}.webp",
                (string) $encoded,
            );
        }
    }
}

Dispatch it from an ImageUploaded event listener so uploads stay fast and the request never blocks on GD/Imagick.

On-the-fly resizing with an HTTP cache — for user-supplied avatars where pre-generating every size is wasteful:

Route::get('/media/{path}', function (string $path, ImageManager $images) {
    $size = (int) request('w', 320);
    abort_unless(in_array($size, [160, 320, 640], true), 404);

    $cacheKey = "derived/{$size}/".md5($path);
    if (! Storage::disk('public')->exists($cacheKey)) {
        $encoded = $images->read(Storage::disk('public')->path($path))
            ->cover($size, $size)
            ->toWebp(75);
        Storage::disk('public')->put($cacheKey, (string) $encoded);
    }

    return response()->file(Storage::disk('public')->path($cacheKey))
        ->header('Cache-Control', 'public, max-age=31536000, immutable');
})->where('path', '.*');

Whitelist the width parameter — never pass user input straight into scale().

Wrap it in your own service, not the framework's. The bundle's job is to hand you an ImageManager; your job is the domain logic. A thin ImageProcessor class that owns naming conventions, disk choices, and quality settings is the extension point that survives a driver swap or a move off Intervention entirely.

Driver selection per environment. GD is almost always present and fine for thumbnails; Imagick handles CMYK JPEGs, animated GIFs, and colour profiles better. Configure it once:

// config/services.php or a dedicated config/image.php
'driver' => env('IMAGE_DRIVER', 'gd'),

and resolve GdDriver / ImagickDriver in the singleton closure.

Store encoded output as a string, not a resource. (string) $encoded or ->save($absolutePath). Mixing Storage::put with an Intervention object and expecting implicit casting is a common source of zero-byte files.

If you are on the Symfony side and upgrading to v1.0.0: treat it as a fresh integration, not a patch. Read DependencyInjection/Configuration.php against your existing config/packages/*.yaml, diff the service IDs, and run composer update janwebdev/symfony-intervention-image-bundle --dry-run -v first. A version reset can silently reorder constraints.

Gotchas and Tips

  • The v1.0.0 release is not evidence of maintenance. The notes say only "initial release" — no changelog, no upgrade guide, no statement about Intervention 3 support, PHP versions, or Symfony versions. Single maintainer, previously zero stars and zero dependents. Before you relax the "abandoned" verdict, confirm the release actually contains modern constraints; a re-tag with the same composer.json is not a revival. Read the repo, not the tag name.
  • Version-constraint whiplash after a version reset. If a legacy Symfony app pinned ^2.0, ~0.x, dev-master, or a specific 2022 tag, a new 1.0.0 tag may not satisfy it — or may satisfy a ^1.0 you'd previously written off. Run composer why-not janwebdev/symfony-intervention-image-bundle 1.0.0 and composer update --dry-run before touching anything; a surprise major bump here can cascade into symfony/framework-bundle and PHP constraint changes you did not budget for.
  • It still cannot conflict-resolve into a Laravel app cleanly. symfony/framework-bundle pulls symfony/http-kernel, symfony/routing, symfony/event-dispatcher — components Laravel also uses indirectly, at different versions. You may get composer solver noise and you will definitely get autoload/Container class duplication risk. A new tag does not fix this; nothing short of a Laravel service provider would.
  • v2 vs v3 API drift is the #1 source of copy-paste bugs — and the new tag makes it harder, not easier, to guess. Every Stack Overflow answer written before 2023 uses Image::make($path)->resize(300, null, fn($c) => $c->aspectRatio()). In v3 that is $manager->read($path)->scale(width: 300). With the bundle freshly re-tagged you can no longer infer the major version from the release date. Check composer show intervention/image and the bundle's own constraint; treat every snippet as suspect until you do.
  • Memory. A 6000×4000 JPEG decodes to roughly 6000 × 4000 × 4 bytes ≈ 96 MB before any resize. Either bump memory_limit for queue workers (php -d memory_limit=512M artisan queue:work) or reject oversized uploads at validation with dimensions:max_width=...,max_height=.... Validate getimagesize() before decoding in on-the-fly endpoints, or you've built a DoS vector.
  • EXIF orientation. Phone photos come back sideways unless you call ->orientate(). Do it before scaling, and strip metadata on output (->strip() in v3, or the save(..., strip: true)/encoder options in v2) so you don't leak GPS coordinates from user uploads.
  • Storage::put() with an encoded image. Storage::put($path, $image->encode()) silently writes garbage or an empty file in some Intervention versions. Always cast to string or use absolute-path save().
  • GD vs Imagick output differs. WebP quality, sharpening, and transparency handling vary. Don't write pixel-comparison tests; assert dimensions, format, and non-zero filesize instead. For golden-image tests, pin the driver in phpunit.xml.
  • Debugging checklist: php -m | grep -E 'gd|imagick', confirm imagewebp is in gd_info(), php artisan tinker → app(ImageManager::class)->read(...), and log $encoded->mime() plus strlen((string) $encoded). If a variant is 0 bytes, the write failed, not the transform. On the Symfony side, add bin/console debug:container ImageManager and debug:config <bundle_alias> to confirm the extension actually loaded after the version bump.
  • Extension points worth knowing: the singleton binding in AppServiceProvider is your swap point (you can bind a decorated manager, a driver selected by config, or a fake in tests). If you outgrow that, implement your own Thumbnailer/ImageProcessor contract and keep Intervention as an implementation detail — nothing in your controllers should type-hint Intervention\Image\*. On Symfony, the equivalent swap point is a service alias or decorator over the bundle's ImageManager service ID.
  • If you genuinely need Symfony-side wiring (mixed repo, shared service definitions), read DependencyInjection/Configuration.php first and copy the config keys into config/packages/*.yaml. But for a Laravel codebase, deleting this dependency and writing the 15-line service provider is still the higher-value move — it removes three Symfony packages and an unvetted maintainer from your supply chain. The v1.0.0 tag, on its own, does not change that arithmetic.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
cgv/localized-resource