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

File Laravel Package

amethyst/file

A lightweight file handling component for Amethyst that wraps Laravel's filesystem with a fluent API for uploads, disk paths, mime types, and file metadata. A clean, reusable building block for projects that need consistent file management.

View on GitHub
Deep Wiki
Context7

Getting Started

amethyst/file is a Laravel-oriented file/attachment layer: a files table plus Eloquent traits/attributes that let a model own uploaded files (and image variants) without hand-rolling storage, naming, and cleanup on every project.

Install and inspect

composer require amethyst/file
php artisan vendor:publish --provider="Amethyst\File\FileServiceProvider"   # check the actual provider name in src/
php artisan migrate

Auto-discovery should register the provider; if not, add it to config/app.php manually. After publishing, open config/amethyst/file.php first — the disk name, base path, and URL generation live there, and everything else follows from those three values.

Where to look, in order

  1. src/FileServiceProvider.php — what gets bound, what migrations/config are published.
  2. src/Attributes/ (or src/Traits/) — the trait/attribute classes you actually attach to models. This is the public API surface.
  3. database/migrations/ — the schema you inherit (files table shape: name, path, size, mime, fileable morph columns).
  4. config/amethyst/file.php — disk and path conventions.
  5. tests/ — with a 0-star package this is your only reliable spec; read it before trusting the README.

First use case: attach an upload to a model

The intended day-one flow is: a model uses the file trait, declares which attributes are files, and the package persists the upload and returns a URL.

use Amethyst\File\Traits\FileTrait;   // confirm exact name in src/

class Article extends Model
{
    use FileTrait;

    protected $fileAttributes = ['cover', 'attachment'];  // confirm the property/contract
}

Then bind an UploadedFile to that attribute in a controller or form request, save the model, and read the URL back off the attribute. If that loop works end-to-end in a scratch project, you've validated the only thing that matters before committing.

Implementation Patterns

Treat the model attribute as the boundary. Keep $request->file('cover') out of your domain code: accept a UploadedFile, hand it to the package's attribute, and let Eloquent persistence own the write/delete lifecycle. Everything downstream reads a URL or a File model, never a temp path.

One trait per model, explicit attribute list. Don't infer file columns from the schema — enumerate them. It makes the delete-and-replace path predictable and keeps you honest when a column is added later.

Replacement and cleanup. The standard workflow is: save new file → package writes new path → old File row/path is removed on model save/delete. Verify this with Storage::fake() in a test rather than assuming; orphan cleanup is the single most common place these packages have gaps. If it doesn't clean up, add an observer:

static::deleted(fn ($model) => $model->files->each->delete());

Wrap it in a thin adapter. Because the package is a black box with no downstream users, do not let Amethyst\File\* types leak into controllers, API resources, or jobs. A small FileStorage/Attachment interface with store(), url(), delete() means swapping to spatie/laravel-medialibrary later is a day, not a rewrite.

Storage disks per environment. local in dev, S3 in production, same config key. Use Storage::disk(config('amethyst.file.disk'))->url($path) for public URLs; keep private files on a private disk and serve through a signed-route controller.

API resources. Expose a stable shape from your own resource class:

return [
    'url'  => $this->cover?->url(),
    'size' => $this->cover?->size,
    'mime' => $this->cover?->mime,
];

Async work. Thumbnails, image resizing, or virus scanning belong in a queued job that receives the File model's ID, not the UploadedFile. Upload, save, dispatch.

Testing. Storage::fake('public') plus UploadedFile::fake()->image('a.jpg'), then assert the file exists on the fake disk and the row exists. Assert deletion removes the stored object too — that's your regression net against package quirks.

Integration tip: if you already use intervention/image directly, check whether the package pins a version. Two Intervention majors in one app is a painful, avoidable conflict.

Gotchas and Tips

Zero stars, zero dependents is the headline risk. No community means no Stack Overflow answers, no issue triage, and no warning when a Laravel minor breaks it. Pin the exact version in composer.json ("amethyst/file": "1.0.*"), and add a smoke test that exercises upload → save → delete so composer update failures surface in CI rather than production.

Check PHP and Laravel constraints before anything else. composer show amethyst/file — if illuminate/support is capped below your Laravel version, stop. This package predates or lags current Laravel releases in all likelihood.

Config caching will bite you. php artisan config:cache freezes amethyst.file.disk and paths. If uploads land in the wrong place after deploy, clear the cache before debugging the code.

Disk must exist in config/filesystems.php. The package reads a disk name; a typo yields a Storage exception that looks like a package bug but isn't. Also confirm php artisan storage:link has been run for the public disk.

Public vs private URL semantics. local disks return /storage/... URLs only if the symlink exists; s3 returns full URLs only with the right visibility. Test both, or ship a bug that only shows in production.

Mass assignment and casts. If the file columns live on the parent model, they'll appear in $fillable-adjacent logic; guard them. If the package stores them in a morph table, remember ->with('files') to avoid N+1 on listings.

Path collisions and naming. Most of these packages generate names from the original filename or a hash. Confirm the strategy — filename-based schemes collide across tenants and leak user input into your filesystem.

Orphaned files on failed validation or abandoned forms. If the write happens before validation passes, you accumulate garbage. Validate first, store second, and consider a scheduled command that diffs files rows against disk contents.

Transaction boundary. Wrap the model save and file write in DB::transaction, but know that filesystem writes don't roll back. Accept that a failed transaction can leave an orphan, and make your cleanup job idempotent.

Extension points to look for. The provider's container bindings are your leverage: rebind the file repository or the URL generator to change behavior without forking. If there's no binding for what you need, that's a signal to wrap rather than patch — forks of unmaintained packages rot fast.

Debugging checklist when a file "doesn't upload": (1) is the provider registered; (2) does the disk exist; (3) is the attribute listed in the model's file-attribute config; (4) is the request field name matching; (5) does the files row exist but the disk object missing (path generation bug)? Check the row and the disk separately — they fail independently.

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.
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
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
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core