arxy/files
Arxy Files simplifies file management in PHP apps with FlySystem: store files anywhere, persist metadata in the database, and deduplicate uploads using MD5 checksums. Automatic upload/delete hooks and flexible naming strategies keep entities in sync.
Architecture fit — the premise needs correcting first. arxy/files is not a Laravel package; it is a Symfony bundle built around a Doctrine ORM persistence layer. That single fact drives the entire assessment. Its defining value propositions are Symfony/Doctrine-native:
postPersist, preRemove) — the README is explicit that files are staged and only moved to their final location when the entity is flushed. This hooks into Doctrine's unit of work; Eloquent has no equivalent event contract (saved/deleted model events fire at different points and with different transaction semantics).arxy_files configuration tree, named manager definitions (driver/class/storage/naming_strategy/repository), Twig extension, Symfony Form type, and Symfony Serializer/LiipImagine normalizers.The part that is portable is the core: Manager, ManagerInterface, FlysystemStorage, the NamingStrategy decorators, and the Repository interface. The README's "pure PHP" example confirms these classes can be constructed by hand with no container. That is the only seam worth leveraging in Laravel.
Integration feasibility. Two viable paths, with very different economics:
laravel-doctrine/orm) — the bundle's semantics survive, but the Symfony DI configuration layer (arxy_files, the compiler-pass-driven manager factory) will not load. You would bypass it and hand-wire services in a ServiceProvider, replicating what the bundle's extension does. Feasible, but you inherit all of Doctrine's ceremony to get file uploads.FlysystemStorage and the naming strategies. Laravel's Storage facade is already a Flysystem wrapper, so FlysystemStorage would need to wrap a Storage::disk() instance or you drop it entirely.Technical risk. Rated high for adoption, moderate for vendoring:
opportunity: 14.98 and overall score of 25.4 are consistent with "interesting internals, no adoption."2026-09-30, which is in the future. Treat this as unreliable provenance and verify the actual commit history before any dependency decision — it may indicate a re-tagged repo or metadata artifact.symfony/framework-bundle would drag Symfony's container, HTTP kernel, and event dispatcher into a Laravel app. Composer audit is mandatory before adoption.preRemove deletes the object on entity removal with no visible reference counting, so two entities sharing one file hash can break each other. Cross-tenant dedupe is also a data-isolation concern (hash disclosure, existence oracles). MD5 is fine as a content fingerprint, not as a security control.Key questions to answer before a decision:
PersistentPathStrategy + UUID names)?intervention/image would be the analogue).Stack fit — stated plainly. Against a Laravel/Eloquent codebase this is a poor fit as a dependency and a good fit as a design reference. Against a Symfony codebase (or Laravel+Doctrine) it is a reasonable fit. The assessment below assumes Laravel/Eloquent, since that is the most likely reading of the brief.
What to reuse vs. replace:
| Component | Verdict |
|---|---|
Manager / ManagerInterface |
Reuse (portable, container-free) |
FlysystemStorage |
Reuse, or replace with a thin adapter over Storage::disk() |
NamingStrategy family (SplitHash, Date, UuidV5, AppendExtension, PersistentPath, decorators) |
Reuse — this is the most cleanly extracted value in the package |
Model\File / Entity\File / EmbeddableFile |
Reimplement as an Eloquent model + migration |
Repository\ORM trait |
Reimplement against Eloquent |
DoctrineORMListener |
Replace with Eloquent observers or queued jobs |
Form\Type\FileType, constraints |
Discard; use Laravel validation rules |
Twig\FilesExtension |
Discard; Blade equivalent if needed |
Serializer normalizers, DownloadableFile, DownloadUtility |
Discard; use API Resources + Storage::temporaryUrl() |
arxy_files DI extension |
Discard; write a FilesServiceProvider |
Migration path (if any existing file handling exists):
files table: id, md5_hash, mime_type, size, path/name, created_at) alongside legacy columns.PersistentPathStrategy-style path column so nothing breaks on cutover.Compatibility gates to clear in Phase 0:
symfony/* constraints in composer.json (looking specifically for framework-bundle, which would make this a non-starter).league/flysystem (Laravel 10/11 ship Flysystem 3).composer why symfony/framework-bundle against a scratch Laravel app before committing.Sequencing (recommended):
Manager manually inside a Laravel ServiceProvider, point FlysystemStorage at a local disk, upload and read one file. Output: go/no-go on reuse vs. rewrite. My prior is that this yields a rewrite decision under Eloquent.md5_hash (scoped by tenant if applicable), Manager binding, naming strategy selection.postPersist/preRemove, but with the ordering corrected: write the object then commit the row, or use a pending status column and a reconciliation job. Do not copy the "stage then move on persist" model directly — see failure modes below.file_references join or a ref_count column) before enabling deletion; a scheduled orphan sweep.DownloadUtility entirely; a PHP streaming controller will be the first thing to fall over under load.Alternative recommended path: if the team is Eloquent-native, write the file subsystem in-house. The scope is small (one model, one manager, one observer, one service provider, one config file), the failure semantics are yours to control, and you avoid a Symfony-shaped dependency in a Laravel app. The bundle's naming strategies are ~100 LOC each and straightforward to reimplement; use them as specification.
Maintenance. This is the dominant cost. With 18 stars and zero dependents, upstream is effectively a single-maintainer project with no evidence of a contributor bench. Plan for one of two states: (a) pinned-commit dependency with an internal patch queue, or (b) hard fork owned by our team. Both require a named owner. Budget an annual maintenance line for Symfony/Flysystem/Doctrine major-version alignment — the bundle sits on three fast-moving abstractions simultaneously. MIT licensing makes the fork legally trivial; the cost is purely engineering time.
Support. No commercial support, no upgrade guides, and documentation limited to the README. Onboarding material must be written internally, particularly around the counterintuitive staging contract: the file is not in its final location until the entity is flushed. That is a genuinely surprising behavior for anyone raised on $request->file()->store(), and it is exactly the kind of thing that produces "the file uploaded but the URL 404s" tickets.
Scaling.
files table becomes write-hot and the md5_hash index becomes the contention point — concurrent identical uploads will race the unique constraint. Handle it: catch the duplicate-key exception and re-fetch rather than surfacing a 500.Failure modes to design against explicitly:
postPersist. If the object write fails after the DB commit, the row is durable and the object is not. Mitigation: pending/ready status column plus a reconciliation job.preRemove deletes the object by hash. Two rows sharing that hash, one removed → the other is now dangling. There is no reference counting in the package. This is the highest-severity design gap and must be solved before enabling dedupe in production.PersistentPathStrategy) or a migration maps old→new keys. Treat naming strategy as a schema-level commitment.Ramp-up. Small surface area — an experienced Laravel engineer can read the relevant classes in an afternoon. The friction is conceptual, not volume: the Doctrine lifecycle semantics, the deferred-write contract, and the dedupe reference model all require explicit documentation. Estimate one engineer-week to reach productive contribution, plus a half-day of written runbook covering the staging contract, the unique-constraint race, and the orphan-sweep procedure.
Bottom line. For a Symfony or Laravel+Doctrine stack: adoptable, with the caveat that you own the fork. For a Laravel/Eloquent stack: do not add this as a dependency — extract the naming strategies and the storage-abstraction pattern as a design reference, and build the ~500-LOC subsystem in-house under Eloquent so that lifecycle, dedupe reference counting, and failure recovery are under our control. The strongest signal in the evaluation is not the code quality (which reads as competent, well-tested, statically analyzed) but the adoption profile: 18 stars, 0 dependents, single maintainer, and a future-dated release. That is a supply-chain and bus-factor problem, not a technical one.
How can I help you explore Laravel packages today?