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

Guided Image Laravel Package

reliqarts/guided-image

A Laravel package that simplifies image handling with a guided, config-driven API for resizing, cropping, and generating image variants. Built on Intervention Image, it streamlines responsive image transformations and common manipulation tasks in PHP applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and its image driver dependency:

composer require reliqarts/guided-image

The package wraps intervention/image, so you also need a PHP image backend (GD or Imagick). Confirm which extensions are enabled with php -m | grep -Ei 'gd|imagick'. Most Laravel apps already have GD available.

Publish the config (tag name can vary by version — check the README if the tag fails):

php artisan vendor:publish --provider="ReliqArts\GuidedImage\ServiceProvider"

Look at these three things first, in order:

  1. config/guided-image.php — driver, default quality, cache/config paths. This is where almost every behavioral quirk lives.
  2. The GuidedImage facade and its underlying service class — this is the actual surface area you'll call. Skim the public methods; the package is intentionally thin.
  3. The README examples — the intended happy path is a one-liner chain.

The first useful thing to build is the classic "upload → resize → store" flow for a user avatar:

use ReliqArts\GuidedImage\Facades\GuidedImage;

$file = $request->file('avatar');
$path = 'avatars/' . $user->id . '.jpg';

GuidedImage::make($file)
    ->fit(200, 200)
    ->save(storage_path('app/public/' . $path));

That is the core value proposition: you don't hand-roll Intervention\Image\ImageManager wiring, you get a facade and sensible defaults. If your first task is anything more complex than resize/crop/fit/rotate, jump straight to Intervention Image and treat this package as optional sugar.

Implementation Patterns

Chained manipulation with a single terminal call. The idiomatic use is a fluent chain ending in save() (or a combined resizeAndSave() / cropAndSave() helper if exposed by your version). Keep the chain short — one operation per logical intent makes failures easy to locate.

GuidedImage::make($uploadedFile)
    ->resize(1200, null)   // preserve aspect ratio
    ->save($destination, 85);

Derivative generation in a dedicated service. Don't scatter image logic across controllers. Create an ImageService or a small action class that takes an UploadedFile (or path) and returns an array of generated paths (thumb, medium, large). This keeps the package confined to one layer and makes it trivial to swap for raw Intervention later.

class ImageVariants
{
    public function generate(UploadedFile $file, string $base): array
    {
        $variants = [
            'thumb'  => [150, 150],
            'medium' => [600, 600],
            'large'  => [1200, 1200],
        ];

        $paths = [];
        foreach ($variants as $name => [$w, $h]) {
            $path = "{$base}-{$name}.jpg";
            GuidedImage::make($file)->fit($w, $h)->save(storage_path("app/public/{$path}"));
            $paths[$name] = $path;
        }

        return $paths;
    }
}

Move heavy work to a queued job. Image manipulation is CPU- and memory-bound. The package is synchronous; wrap it in a job with $timeout and $tries set, and pass a path rather than an UploadedFile (uploaded file temp paths are gone by the time the worker runs — store the original first, then dispatch).

ProcessImageJob::dispatch($storedOriginalPath, $variantSpec);

Integrate with Eloquent via an accessor or a small trait of your own. The package does not need to know about models. Keep the coupling at the model boundary:

public function getAvatarUrlAttribute(): string
{
    return Storage::disk('public')->url($this->avatar_path);
}

Fall through to Intervention for anything advanced. Watermarks, canvas, text drawing, EXIF-based auto-orientation, format conversion beyond what the wrapper exposes — resolve Intervention\Image\ImageManager from the container (or call GuidedImage::getImage() if the wrapper exposes the underlying instance) and use the native API. Don't fight the wrapper.

Centralize destination paths and disk mapping. Because the package works with filesystem paths, not Laravel Storage disks, write one helper that converts Storage::disk('public')->path($relative) and use it everywhere. This prevents the "works locally, breaks on S3" class of bug.

Gotchas and Tips

Intervention Image v2 vs v3. This is the biggest risk. Intervention 3 rewrote its API (driver objects, ImageManager::gd(), read()/encode() instead of make()/save()), and a small wrapper package may pin ^2.x. If your app already pulls Intervention 3, composer may either conflict or silently install v2 alongside. Check composer show intervention/image and the package's composer.json constraints before assuming the API. This alone is a reason to pin exact versions in composer.json.

Saving to Storage disks doesn't work directly. With S3 or any non-local disk, save() needs a local temp path. Pattern: write to storage_path('app/tmp/...'), then Storage::disk('s3')->putFileAs(...) and unlink the temp file. Do not pass Storage::url() output into the package.

Absolute vs relative paths. The wrapper typically expects a real filesystem path. public_path('images/x.jpg') works; 'images/x.jpg' may resolve relative to the process CWD, which differs between web and queue workers. Always use storage_path() / public_path() / Storage::path().

Extension and format are inferred from the destination filename, not the source. If you save('avatar') without an extension, behavior is driver-dependent and may error. Always include the extension and match it to what you intend (.jpg for photos, .png for transparency).

EXIF orientation is not automatic. Phone photos will come out rotated. Call ->orientate() (Intervention method, may be pass-through via the wrapper) before resize/fit, or better, normalize orientation at upload time.

Memory limits surface as silent failures or blank images. Large JPEGs (12MP+) can exceed the default 128M memory_limit, especially with Imagick. Raise memory_limit in the queue worker and CLI context, and consider ini_set('memory_limit', '512M') inside the job. Test with a real 4000×3000 image, not UploadedFile::fake()->image() (which generates tiny images and hides this).

GD vs Imagick behavior differs. Transparency, color profiles, and GIF handling vary. Set the driver explicitly in config rather than relying on whatever's installed, and make sure your Docker/CI image matches production. Animated GIFs are essentially unsupported by GD.

Default quality may not be what you expect. Check the config value before shipping; many wrappers default to something conservative (e.g. 75 or 90). Set it explicitly per save() call for user-facing hero images.

save() typically overwrites silently. If you need idempotency or want to skip regeneration, check file_exists() first. There's no built-in caching or "only if changed" behavior — that's your job.

Testing. Use UploadedFile::fake()->image('x.jpg', 800, 600) and Storage::fake('public'). Assert existence and dimensions via getimagesize() or Intervention rather than byte equality, since encoder output varies across driver versions. Don't assert on exact bytes — it will break on CI with a different libjpeg.

Extension points and the escape hatch. The package binds a concrete service in its provider. If you need custom behavior (custom naming, logging, retries), bind your own implementation behind an interface and inject that — don't fork the package. Given 34 stars and 0 dependents, treat it as a convenience layer: pin the version, keep usage confined to one service class, and be prepared to replace it with ~30 lines of direct Intervention code if it stalls or conflicts with an Intervention upgrade.

Clear config cache after publishing. php artisan config:clear — the number of "why isn't my driver change taking effect" reports on small packages is high, and it's almost always bootstrap/cache/config.php.

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.
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
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