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

Lio Laravel Package

bvtterfly/lio

Lio is a Laravel package by bvtterfly that provides a clean, Eloquent-friendly API with sensible defaults and minimal configuration, making it easy to integrate into existing Laravel applications and workflows.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish the config

composer require approached/laravel-image-optimizer
php artisan vendor:publish --tag=laravel-image-optimizer

Auto-discovery registers the service provider; there is nothing to bind manually unless you want to swap implementations in tests.

Look at the config file first. config/laravel-image-optimizer.php is where all day-to-day behaviour lives. The optimizers array is an ordered chain — each entry is a class handling specific MIME types, and the chain runs until every supported type has been attempted. The default set mixes local binaries (jpegoptim, optipng, pngquant, gifsicle, svgo) with API-based optimizers (reSmush, tinyPNG, kraken).

First use case — optimize an uploaded avatar after validation:

use Approached\LaravelImageOptimizer\ImageOptimizer;

public function store(Request $request, ImageOptimizer $optimizer)
{
    $path = $request->file('avatar')->store('avatars', 'public');
    $full = Storage::disk('public')->path($path);

    $optimizer->optimizeImage($full);

    return back()->with('status', 'Uploaded');
}

Choosing your first optimizer. If you are on shared hosting or Docker without image tooling, start with reSmush alone — it is pure HTTP, no binaries. If you control the server, start with the local lossless pair (jpegoptim + optipng) instead; it is faster, deterministic, and has no rate limits.

On 2.0.1 specifically: this is a patch release fixing bugs in the reSmush optimizer. There is no API change, so upgrading from 2.0.0 is safe — but do upgrade if reSmush is enabled, because the fixed bugs were in the path you would otherwise be silently relying on. Pin ^2.0.1 rather than ^2.0.

Implementation Patterns

Never optimize inside the request/response cycle. Large PNGs plus a remote API call will blow past max_execution_time and hold a PHP-FPM worker. Dispatch a job:

OptimizeImage::dispatch($fullPath)->onQueue('images');

Give that job $tries = 3 and a backoff(), and treat exhaustion as non-fatal — the image still exists, just unoptimized.

Order the chain lossless-then-lossy. The config order is the execution order. A practical chain: jpegoptim --strip-all → optipng -o2 → pngquant/reSmush for the last mile. Putting a lossy optimizer first means later steps re-compress already-degraded pixels for no gain.

'optimizers' => [
    Approached\LaravelImageOptimizer\Optimizers\JpegOptimOptimizer::class,
    Approached\LaravelImageOptimizer\Optimizers\OptiPngOptimizer::class,
    Approached\LaravelImageOptimizer\Optimizers\PngQuantOptimizer::class,
],

Custom optimizers are the extension point. Extend the package's abstract optimizer (or implement its optimizer contract), return the supported MIME types, and shell out via Symfony Process. Then register the class in the config array — nothing else needs to change. This is how you add cwebp/AVIF conversion or an internal image service.

Preserve originals, then compare. The optimizer mutates files in place. Copy to a originals/ disk first, then keep whichever is smaller:

$before = filesize($full);
$optimizer->optimizeImage($full);
if (filesize($full) >= $before) {
    Storage::disk('originals')->put($path, file_get_contents($fullPath)); // restore
}

Wire it to model events, not controllers. A ImageObserver on saved/created keeps every ingestion path (API, Nova, imports, seeders) covered instead of just the web form.

Backfill existing media with an artisan command. php artisan images:optimize --disk=public --chunk=100 walking the disk is the standard one-off migration. Log the byte savings per file so you can report the actual win.

Testing. Never let a real binary or a real API call run in CI. Bind a pass-through fake into the container or point the config at a no-op optimizer class in phpunit.xml, and assert your job was dispatched rather than asserting on file bytes.

Docker/CI image. Local optimizers need jpegoptim optipng pngquant gifsicle from the distro package manager and svgo from npm. Add a smoke test that runs which jpegoptim in the built image — the failure otherwise shows up only at runtime on a user's upload.

Gotchas and Tips

  • reSmush is a third-party HTTP service. It has a per-file size ceiling (roughly 5 MB), rate limits, and occasional outages. Always wrap the call in try/catch, log the failure, and let the upload succeed with the original file. Optimization is best-effort, never a hard dependency.
  • The 2.0.1 fix can surface new exceptions. If 2.0.0 was swallowing a failure that you were implicitly depending on, you may now see exceptions where you previously saw silence. Re-run your upload tests after upgrading and make sure the call site has a catch.
  • "Binary not found" throws at runtime, not at boot. Missing optipng on a freshly provisioned server breaks the first upload, not the deploy. Add a php artisan image-optimizer:check style health probe or a scheduled check.
  • Config caching. php artisan config:cache after every config edit; a stale cache is the most common "my new optimizer isn't running" cause.
  • Permissions. PHP-FPM/queue workers must be able to write the target file. Files created by a root-run console command will fail optimization from the queue user.
  • Timeouts. Remote optimizers can hang for tens of seconds on a slow network. Configure the process/HTTP timeout low enough that a stuck optimizer does not pin a queue worker, and set retry_after on the queue above your worst-case job duration.
  • EXIF stripping is a decision, not a default. --strip-all / reSmush remove metadata including orientation and GPS. That is usually what you want for GDPR and bytes — but confirm you are not shipping sideways phone photos, and that you strip after any orientation read you need.
  • Verify the saving, don't assume it. Some lossy optimizers can grow an already-optimized file. Compare filesize() before and after and keep the smaller copy.
  • Do not run it inside a DB transaction. A slow optimizer holds row locks and a connection far longer than intended. Optimize first, or better, in a queued job after commit.
  • Debugging. Check the raw output of the underlying tool first (jpegoptim --version, curl the reSmush endpoint manually), then confirm the optimizer actually claims the file's MIME type, then confirm the chain order. Most "it does nothing" reports are a MIME mismatch or a cached config.
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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect