vormkracht10/flysystem-uploadcare
A Flysystem adapter that lets Laravel apps use Uploadcare as a storage disk. Register it as a disk and use the familiar Storage facade to upload, retrieve, and delete files through Uploadcare's CDN-backed file handling service with minimal configuration.
vormkracht10/flysystem-uploadcare is a Flysystem v3 adapter that plugs Uploadcare into Laravel's Storage facade, so you get put/get/url/delete against Uploadcare's file API and CDN without hand-rolling SDK calls.
Minimal setup:
composer require vormkracht10/flysystem-uploadcare
// config/filesystems.php
'uploadcare' => [
'driver' => 'uploadcare',
'public_key' => env('UPLOADCARE_PUBLIC_KEY'),
'secret_key' => env('UPLOADCARE_SECRET_KEY'),
],
The service provider is auto-discovered and registers the uploadcare driver — there is no migration, no filesystem symlink, and no local disk involved.
Where to look first: the package's config file (publish it if the README tells you to, since the SDK/CDN options live there), then the driver registration in the service provider, then Uploadcare's own dashboard — the project settings there (public/private storage, CDN base URL) affect behaviour more than any PHP config key.
First use case: user-generated media. Store an avatar and immediately persist the returned path:
$path = $request->file('avatar')->store('avatars', 'uploadcare');
$user->update(['avatar' => $path]);
Storage::disk('uploadcare')->url($path); // https://ucarecdn.com/<uuid>/...
The 0.6.0 release is purely a compatibility widening (illuminate/support now allows ^13.0, Pest ^4.0 / PHPUnit ^12.0, CI gains Laravel 13/Testbench 11 and PHP 8.4). Nothing in your application code changes on upgrade — this is the release that lets you adopt the package on a Laravel 13 codebase without --ignore-platform-reqs tricks.
Treat the disk as write-once, read-many. Uploadcare is a file store + CDN, not a POSIX filesystem. The happy path is: upload → persist the returned identifier → serve via url(). Anything that mutates or enumerates (overwrite, rename, recursive listing) is where you should reach for the SDK instead of the Flysystem interface.
Always persist what store() returns, never what you asked for. The adapter sends the file to Uploadcare and Uploadcare assigns the canonical identity, so the path you get back is the source of truth. Column type should be a string long enough for a UUID plus filename; don't parse it.
Two disks, one model. Pair uploadcare for media delivery with local/s3 for anything your app needs to enumerate (exports, backups, temp scratch). Code that does Storage::disk('uploadcare')->allFiles() in a loop will be slow and API-metered.
Signed URLs for anything non-public. temporaryUrl($path, now()->addMinutes(30)) is the pattern for private assets; it needs the secret key present. Unsigned CDN URLs never expire, so never treat "the URL stopped working" as an access-control mechanism.
Lean on CDN operations, not PHP. Uploadcare transformations are URL operations, so build them where you render rather than storing derived files:
$url = Storage::disk('uploadcare')->url($path) . '-/resize/400x400/-/quality/smart/';
Keep a small URL-builder helper or accessor on the model so transformation strings aren't scattered across Blade files.
Queue the uploads. In a request cycle the adapter is doing an HTTP round-trip; wrap uploads in a job and let the CDN serve the result. This also keeps you clear of request timeouts on large files.
Testing: Storage::fake('uploadcare') works because faking happens at the Laravel disk layer, so feature tests stay offline. Assert the persisted path exists on the fake, and keep a separate integration test (opt-in, real credentials) if you depend on CDN URL shape.
Upgrade pattern for 0.6.0: if you're on Laravel 11/12, ^0.6 resolves fine and changes nothing at runtime. If you're preparing for Laravel 13, bump the package and your own illuminate/* constraints together, and check that your CI image is on PHP 8.4 (which the new test matrix implies is the supported upper bound). If you maintain a Pest 3 test suite in your app, that's unaffected — Pest 4 is a dev dependency of this package, not a transitive requirement.
put() as a durable key will bite you on the second read. Persist the return value, and backfill/migrate any code that hardcodes a path.directories() and prefix-filtered files() are emulated over a paginated file API — results can be expensive and eventually consistent. Use a database column (owner, folder, tag) for grouping instead of the path hierarchy.exists() and delete() are network calls. In an N+1 loop they'll dominate your response time. Batch deletions via the Uploadcare SDK, and cache existence/size/mtime metadata locally if you render file lists.setVisibility()/visibility args may be ignored or behave counter-intuitively; choose public vs. private in the Uploadcare dashboard and design around it.lastModified() and size() come from the API, not from the bytes you uploaded. They can be absent immediately after upload — don't build redirects or "newest file" logic on them without a fallback.UPLOADCARE_* changes require php artisan config:cache in cached environments; a missing secret key typically surfaces only when you first call temporaryUrl().Storage::extend('uploadcare', ...) if you need to decorate the adapter (logging, path rewriting, default transformation), and publish the package config to tune SDK/CDN options. If you need add-ons (virus scan, image recognise, video encoding), don't fight the adapter — call the Uploadcare PHP client directly from a service class and keep the disk for plain storage."vormkracht10/flysystem-uploadcare": "^0.6" is right for Laravel 13 prep; a bare "*" will silently jump to whatever the next 0.x brings, which for a pre-1.0 package is not a safe assumption.How can I help you explore Laravel packages today?