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

Files Media Laravel Package

liberusoftware/files-media

A Laravel package for handling file and media uploads. Attach images, documents, and other assets to Eloquent models through a simple, consistent API, with support for storage disks, media associations, conversions, and organized file management across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is a modular Laravel component (liberusoftware/module-files-media) that hangs its functionality off contracts rather than a single façade. Treat it as a set of interfaces you bind implementations to, not as a drop-in Upload::store() helper. Because it has zero stars and zero dependents, budget an hour to read the source before wiring it into anything.

First steps

  1. composer require liberusoftware/files-media (confirm the actual require string from the repo's composer.json — module packages frequently ship a different install name than the GitHub slug).
  2. Inspect the service provider. Look for register() bindings and any mergeConfigFrom. That tells you which contract maps to which concrete class and whether config is publishable.
  3. Publish config: php artisan vendor:publish --provider="...FilesMediaServiceProvider" --tag=config (verify the tag name).
  4. Read src/Contracts/ end to end. This is the whole API surface. The package name implies at minimum: a storage contract, a validator contract, a scanner contract, a transformer contract, and an access contract. Everything else is an implementation detail.
  5. Run the package's own test suite locally (vendor/bin/phpunit --testsuite=files-media or php artisan test vendor/liberusoftware/...) before writing your own tests. With no dependents in the wild, their tests are your only reliable spec.

First use case: a single authenticated upload with validation and virus scanning, stored on a private disk.

// Sketch — verify class/contract names in src/Contracts before relying on this
public function store(UploadFileRequest $request)
{
    $file = $request->file('document');

    $validated = $this->validator->validate($file);   // size, mime, dimensions
    $result    = $this->scanner->scan($file);          // async dispatch inside, hopefully

    if (! $result->isClean()) {
        abort(422, 'File rejected by scanner.');
    }

    return $this->storage->store($file, disk: 'private');
}

Where to look first, in order: composer.json (Laravel/PHP version constraints — this is where old modules usually break on Laravel 11+) → src/Contracts → config/ → service provider → migrations (metadata and retention almost always imply tables).

Implementation Patterns

Bind the contracts, don't extend the concretes. The value here is that FilesMedia gives you a seam for swapping a ClamAV scanner for a VirusTotal one, or S3 for local, without touching controllers. In AppServiceProvider::register(), rebind any contract whose default implementation doesn't match your infra:

$this->app->bind(ScannerContract::class, ClamAvScanner::class);
$this->app->bind(StorageContract::class, S3Storage::class);

Two-phase upload for scanning. The description bundles "uploads" and "scanning" — almost certainly the intended flow is: accept the file → persist with a pending/quarantined status → scan asynchronously on a queue → promote to clean or delete. Do not serve files from the metadata table until the scan state is clean. Model this as an explicit state column in your own table if the package doesn't ship one, and gate your download route on it.

Validation belongs in a FormRequest, not the contract. Use Laravel's native rules (mimes:pdf,docx, max:10240, dimensions) for the cheap synchronous checks, and reserve the package validator for domain rules it uniquely knows (allowed extensions per tenant, per-plan size caps). Doing all validation through the package means you lose standard error-bag messages for free.

Transformations as a pipeline, not a fork. Typical pattern: original → thumbnail → web-optimised. If the transformer contract accepts a definition list, treat transformations as derived artifacts keyed off the parent file ID, so a retention policy on the original cascades. Store transformation outputs on a CDN-friendly public disk and keep originals private — mixing the two is the most common leak in file packages.

Access via signed, short-lived URLs. The "access" contract exists for a reason: never expose the disk path. Wrap it in a controller that checks policy first, then asks the package for a temporary URL:

Route::get('/files/{file}', function (File $file) {
    Gate::authorize('view', $file);
    return redirect($this->access->temporaryUrl($file, now()->addMinutes(5)));
})->middleware('auth');

Retention as a scheduled command. The "retention" piece should be driven by a console command you schedule, not by implicit cleanup:

Schedule::command('files:prune-retained')->dailyAt('03:00');

If the package doesn't ship a command, write one against the metadata repository — you need something idempotent you can run manually during an incident.

Integration tips

  • Fire your own domain events around the package calls (FileStored, FileQuarantined) rather than listening to vendor events, so a future package swap is a one-file change.
  • Wrap store-then-persist in a DB::transaction only for the metadata row; the disk write is not transactional. Order: write file → commit row → on failure, delete orphan.
  • In tests, Storage::fake() plus a fake scanner binding gives you fast, hermetic coverage of the whole flow.

Gotchas and Tips

The repository is unproven. Zero stars, zero dependents, and a release date in the future relative to typical package cadence are all signals to read the code, not the README. Pin an exact version in composer.json and vendor the source in your head — you may be the first person to file an issue, and you should be prepared to fork. Check the commit history for whether contracts are stable or still churning; a renamed contract is a breaking change even on a patch release.

Contracts without defaults. Many modular Laravel packages ship interfaces with no concrete binding until you register a driver. If you get "Target [X] is not instantiable" at boot, that's the cause — not a broken install. Check the config file for a default key and confirm the named driver exists.

MIME sniffing vs. extension. Validation that trusts getClientOriginalExtension() is trivially bypassed. Make sure the package validator uses guessExtension()/fileinfo, and that your own rules do too. Test with a .php renamed to .jpg before you ship.

Scanner false positives on archives and PDFs. Zip bombs and encrypted PDFs will stall or fail a synchronous scanner. Enforce a max-uncompressed-size guard and a hard timeout; a hung scan on a web request is a trivial DoS vector.

Queue dependency for scanning. If scanning is queued, a dead queue:work silently leaves files in pending forever — users see an upload that "worked" but never becomes downloadable. Add a pending age monitor and a fallback that flags files stuck beyond N minutes.

Orphaned files on rollback. Storage writes don't roll back. Every failure path after the disk write needs an explicit delete, or your retention job becomes your only garbage collector — and it won't catch rows that were never created.

Retention deletes vs. legal hold. Before enabling pruning, confirm the retention logic respects any "do not delete" flag you add. A daily cron that hard-deletes is very hard to undo; start with soft-delete plus a 30-day grace window.

Large-file memory. Transformations (especially image resizing) on a 50 MB upload can blow memory_limit in a queue worker. Check whether transformers stream or file_get_contents() the whole file — if the latter, cap upload sizes well below your PHP memory limit.

Multi-tenancy and path traversal. If tenant IDs feed into storage paths, sanitise them. store("tenant-{$id}/...") with an unvalidated $id is a path traversal waiting to happen.

Extension points worth knowing before you need them: the scanner binding (swap in a no-op for local dev — never run ClamAV in CI), the storage binding (local in tests, S3 in prod), and the access contract (swap signed S3 URLs for a streaming controller when you need audit logging on every download). Wire all three as bindings in a dedicated FilesMediaServiceProvider in your app so the swap is documented in one place.

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
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core