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

Filesystem Laravel Package

ralphjsmit/filesystem

A Laravel package that wraps filesystem operations in a clean, expressive API. Create, copy, move, delete, and inspect files and directories with readable methods, keeping common disk tasks simple and consistent across your app's storage disks.

View on GitHub
Deep Wiki
Context7

Getting Started

Install with a pinned version, then immediately read the source.

composer require ralphjsmit/filesystem:^1.4

Pin to 1.4.* — 1.4.0 is the release that explicitly added Laravel 10 support (#11), and it's the only compatibility data point that exists. Commit the composer.lock change alongside the code so the decision is visible in review.

There is no service provider, config file, migration, or docs site to discover. This is a thin wrapper over Laravel's Storage facade. Your onboarding path is:

  1. The GitHub README — the usage examples are the entire spec.
  2. src/File.php and src/Directory.php — read every public method. It's a small package; ten minutes gets you the full capability surface.
  3. grep -rn 'Storage::' app/ in your own codebase — that inventory is your addressable surface. If it's under a dozen call sites, seriously consider whether the package is worth the dependency at all.

First use case: pick one internal call site. Admin export generation, seeded demo data, or archival moves between disks are the right starting points — internal, low blast radius, and genuinely multi-step so the readability win is real. Do not start on the customer-facing upload/download path.

Establish the seam before the first call site. The package goes behind your own interface:

// app/Services/FilesystemService.php
interface FilesystemService
{
    public function put(string $disk, string $path, string $contents): void;
    public function get(string $disk, string $path): string;
    public function move(string $fromDisk, string $from, string $toDisk, string $to): void;
    // ... only what the product actually needs
}

Domain code, controllers, jobs, and commands never use RalphJSmit\.... That single rule makes replacement a one-file change instead of a codebase-wide sweep. The interface is the real architectural decision; the package is an implementation detail behind it.

Implementation Patterns

Always name the disk explicitly. The most common source of "works locally, wrong file in production" is a call that silently falls back to config('filesystems.default'). Require a disk parameter at every entry point, even when the product has one disk today. Costs nothing now and prevents a class of bug later.

Model the seam around your operations, not the package's. Don't expose File or Directory objects through your interface. Speak in domain terms — archiveInvoice(), storeAvatar(), exportReport() — and keep the fluent chain inside the implementation. This also means package types never leak into controller signatures or job payloads.

Use it where the chain is genuinely multi-step. Value scales with how many Storage:: calls you collapse. A single Storage::put() is already one line; wrapping it buys nothing and adds an import. Three or four operations on the same path — exists, delete-if-present, write, copy to archive — is where the fluent form earns its keep and where a reviewer notices the diff.

Preserve Storage::fake() in the test suite. Your adapter should still be exercised through Storage::fake('local'). Verify this as an explicit spike, not an afterthought: if the package routes through Laravel's Storage facade, fakes work unchanged; if it touches Flysystem in a way that bypasses the fake, you have degraded test coverage to buy syntax — a stop-the-line finding for an internal-tools dependency.

Jobs and queues: pass primitives, resolve in handle(). Wrap in the adapter, put strings in the job constructor, resolve FilesystemService from the container inside handle(). Never serialize a File or Directory object into a job payload — it embeds package internals in queue storage and turns future swaps into queue-drain problems.

Directory-level cleanup and listing is a good second use case: archive rotation, temp-file sweeping, seeded demo resets. Read what Directory actually offers before designing around it — if it only lists and deletes, build only against that.

Enforce the seam mechanically. An architecture test or a CI grep that fails on RalphJSmit\ appearing outside app/Services/Filesystem/ takes ten minutes and is the difference between a reversible dependency and a scattered one. If the team won't commit to that rule, don't adopt the package.

Gotchas and Tips

Laravel 11+ is unproven. 1.4.0 claims Laravel 10 support and nothing beyond. On Laravel 11, native Storage is the default answer and this package becomes something you must independently validate. Watch the repo on every framework bump — nobody else will.

Fluent chains hide round trips. Every exists(), get(), and put() on S3 or SFTP is a network call; chaining four of them makes it read like one. Invisible on local disk, real latency and per-request cost on remote disks under load. Count the calls in your typical chain before adopting on remote storage; where the count bites, batch or restructure rather than chain.

Exception semantics are unverified — test before relying. Determine whether failures propagate as Flysystem exceptions (UnableToWriteFile, etc.), get wrapped, or are swallowed into a boolean. Your adapter's error handling depends on the answer, and a swallowed failure in an archival move is a silent data-loss path. Deliberately make a path unwritable and assert what you actually catch.

Large and streamed files. Confirm whether read/write take streams or strings only. If string-only, contents load into memory — fine for config and small exports, wrong for hundreds of megabytes. Keep heavy lifting on Storage::/Flysystem and let the package handle small-file convenience paths.

Treat path-traversal handling as unverified. Don't pass request input through to the package without normalizing it yourself, and prefer to generate paths rather than accept them. Another reason to keep this off any upload path where the user controls the filename.

Know where the extension points aren't. No plugin model, no driver registration, no capability beyond the underlying Storage layer. Multipart uploads, signed-URL policy, image transforms, retry logic — different tool. Don't let the package's presence become an argument that storage concerns are "handled."

Document the fallback in the adapter. A short class-level note — version pinned, native Storage equivalent of each method, who reviewed it and when — turns a future swap into an afternoon instead of archaeology. That note is the artefact that makes this dependency safe to hold.

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.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin