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

Statamic Asset Thumbnails Laravel Package

daun/statamic-asset-thumbnails

A Statamic addon that generates and caches thumbnails for your assets, letting you serve properly sized, optimized images in templates and the control panel. Define thumbnail presets in config and reference them with a simple tag instead of hand-building image URLs.

View on GitHub
Deep Wiki
Context7

Getting Started

The 1.2.1 release is mostly hardening, so getting started is unchanged — but the new failure semantics are worth wiring in from day one rather than retrofitting later.

  1. Install and publish config:
    composer require vendor/package
    php artisan vendor:publish --tag=package-config
    
  2. Set credentials and the new download guards in .env:
    PACKAGE_API_KEY=
    PACKAGE_THUMBNAIL_TIMEOUT=10
    PACKAGE_THUMBNAIL_MAX_FILESIZE=5242880
    
  3. Resolve the client via the container (or facade) rather than new-ing it, so config and HTTP client bindings apply.

Look here first: the config file (timeout/max filesize keys and the error-handling section), the package's exception classes (permanent vs transient are now distinguishable), and the service provider bindings if you need to swap the downloader or SDK client.

First use case: generate a thumbnail for an existing image record and persist it to a disk.

use Vendor\Package\Facades\Thumbnail;

$path = Thumbnail::for($image->source_url)
    ->width(400)
    ->store('thumbnails', 'public');

$image->update(['thumbnail_path' => $path]);

Then render with a guard for the missing-file case (see Gotchas):

@if ($image->thumbnail_path && Storage::disk('public')->exists($image->thumbnail_path))
    <img src="{{ Storage::disk('public')->url($image->thumbnail_path) }}" alt="">
@else
    <img src="{{ asset('images/placeholder.png') }}" alt="">
@endif

Implementation Patterns

On-demand in a controller vs. pre-generated in a job. For low-traffic or admin-only surfaces, generate inline and let the new timeout cap the request. For anything user-facing at scale, push to a queued job so a slow upstream API doesn't hold a PHP-FPM worker:

final class GenerateThumbnail implements ShouldQueue
{
    use Queueable;

    public int $tries = 5;

    public function __construct(public int $imageId) {}

    public function handle(): void
    {
        $image = Image::findOrFail($this->imageId);

        try {
            $path = Thumbnail::for($image->source_url)
                ->width(400)
                ->store('thumbnails', 'public');

            $image->update(['thumbnail_path' => $path, 'thumbnail_error' => null]);
        } catch (PermanentApiException $e) {
            // 4xx: retrying will never help. Record and stop.
            $image->update(['thumbnail_error' => $e->getMessage()]);
            $this->fail($e);
        }
        // TransientApiException / connection errors bubble up and the
        // queue's backoff handles the retry.
    }

    public function backoff(): array
    {
        return [10, 30, 120, 300];
    }
}

Branch on error class, not on status codes. 1.2.1's permanent/transient split is the main thing to adopt. Catch the permanent exception to stop retries and surface a user-facing state; let transient ones propagate to the queue or wrap them in your own retry loop for synchronous paths. A 429 and a 503 should not be treated the same as a 404.

Configure the guard rails per context. Short timeout for web requests, longer for jobs — override at call time where the package supports it, otherwise keep a conservative global default and rely on the queue for slow work. Set max filesize based on your largest legitimate source image, not the upstream maximum; it's your cheapest defence against a hostile or misconfigured CDN response filling a disk.

Wrap SDK exceptions at your boundary. The release expects you to handle SDK-level exceptions manually. Don't let vendor SDK exception classes leak into controllers — catch them in a small adapter/service and rethrow as your domain exceptions so callers only depend on two branches (retryable / not retryable).

Guard every read of a stored asset. Treat thumbnail_path in the database as a hint, not a guarantee. Centralise the check in a model accessor or a small ThumbnailUrl helper so the placeholder fallback isn't duplicated across Blade templates, API resources, and sitemap generation.

Test with fakes. Use Http::fake() for the HTTP layer and the package's own fake/mock hooks where available. Assert the two branches explicitly: one test where the fake returns a 4xx and you expect no retry, one where it returns a 5xx and you expect release()/backoff behaviour.

Gotchas and Tips

  • Timeout units. Check whether the config value is seconds or milliseconds, and whether it's connect-only or total. A connect timeout that's shorter than the upstream TLS handshake will produce transient-looking failures that are actually deterministic.
  • Max filesize is checked when? If the package aborts only after the full body is buffered, the guard protects your storage but not your memory. Prefer configurations where the check happens on Content-Length or mid-stream, and keep PHP's memory_limit in mind for large jobs.
  • Queue timeout must exceed download timeout. A worker timeout lower than the package's download timeout kills the job mid-write and leaves orphaned partial files. Set --timeout and retry_after with headroom.
  • Don't blanket-catch. A bare catch (\Throwable) around the download call converts permanent misconfiguration (bad API key, revoked credentials) into silent retry storms. Log the exception class and, where available, the upstream response body — the SDK exception message alone is often unhelpful.
  • Permanent vs. transient edge cases. 408 and 429 are ambiguous; confirm how the package classifies them before assuming they're transient. If they're classified as permanent in this version, handle those explicitly in your adapter.
  • Missing asset files are now guarded, not impossible. The new check prevents a crash on a deleted file, but it doesn't clean up your database. Add a scheduled reconciliation command that nulls thumbnail_path where the file is gone, rather than leaving dead references forever.
  • Storage visibility. Thumbnails written to public need the symlink (php artisan storage:link) on deploy; if you store to a private disk, generate temporary URLs instead of assuming url() works.
  • Extension points. If the defaults don't fit, the usual levers are: rebinding the downloader/client in a service provider, extending the package's exception handler to map your own error taxonomy, and overriding config via config() at runtime for per-job settings. Prefer these over forking.
  • Debugging downloads. Enable the package's log channel (or wrap it in a Log::withContext call with the source URL and image ID) before reproducing failures; the timeout and filesize rejections are otherwise easy to mistake for empty responses. Laravel's HTTP client events and Telescope are useful if the package is built on the HTTP client and emits them.
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.
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
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
cgv/localized-resource