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 Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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:

  • Auto-lifecycle via Doctrine event listeners (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).
  • Symfony DI extension for the arxy_files configuration tree, named manager definitions (driver/class/storage/naming_strategy/repository), Twig extension, Symfony Form type, and Symfony Serializer/LiipImagine normalizers.
  • Doctrine-only persistence — the README states "Currently, only Doctrine ORM is supported as persistence layer." There is no Eloquent, query-builder, or generic PDO repository shipped.

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:

  1. Laravel + Doctrine ORM (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.
  2. Laravel + Eloquent — this becomes a reference architecture, not a dependency. You would reimplement the Manager/repository/lifecycle hooks (~400–600 LOC) while possibly reusing 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:

  • Maturity signals are weak. 18 stars, 0 dependents, single-maintainer project, no ecosystem validation. The opportunity: 14.98 and overall score of 25.4 are consistent with "interesting internals, no adoption."
  • Metadata anomaly. The release date is listed as 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.
  • Framework churn exposure. Any transitive hard dependency on symfony/framework-bundle would drag Symfony's container, HTTP kernel, and event dispatcher into a Laravel app. Composer audit is mandatory before adoption.
  • MD5-based dedupe is a design decision with real consequences. It saves storage but introduces shared-blob semantics: 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.
  • Licensing is clean. MIT permits vendoring and forking; retain the copyright notice.

Key questions to answer before a decision:

  1. Is the target stack genuinely Eloquent, or is Doctrine ORM already in play? This alone flips the verdict.
  2. Are we adopting a dependency or mining a reference design? I would push hard for the latter under Eloquent.
  3. Do we need per-tenant dedupe scoping, or can we disable dedupe entirely (PersistentPathStrategy + UUID names)?
  4. What is our object store (S3/GCS/local) and do we need signed URL serving rather than a PHP controller?
  5. Do we need virus scanning, EXIF stripping, or image derivatives? The bundle assumes LiipImagineBundle for transforms — irrelevant to Laravel (intervention/image would be the analogue).
  6. Who owns the fork if we take one, and what is the upgrade budget across Symfony 7 / Flysystem 3 / Doctrine ORM 3?

Integration Approach

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):

  1. Inventory current upload paths and existing object keys.
  2. Introduce the new schema (files table: id, md5_hash, mime_type, size, path/name, created_at) alongside legacy columns.
  3. Backfill by hashing existing objects and inserting metadata rows; keep legacy keys resolvable via a PersistentPathStrategy-style path column so nothing breaks on cutover.
  4. Add a dual-read window where the resolver checks new metadata then falls back to legacy.
  5. Cut writes to the new path last, once reads are confirmed stable.

Compatibility gates to clear in Phase 0:

  • PHP version floor and the symfony/* constraints in composer.json (looking specifically for framework-bundle, which would make this a non-starter).
  • Flysystem major version alignment with Laravel's league/flysystem (Laravel 10/11 ship Flysystem 3).
  • Doctrine ORM 2 vs 3 if the Doctrine path is chosen.
  • Run composer why symfony/framework-bundle against a scratch Laravel app before committing.

Sequencing (recommended):

  • Phase 0 — Spike (2–3 days). Construct 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.
  • Phase 1 — Storage + metadata. Eloquent model, migration, unique index on md5_hash (scoped by tenant if applicable), Manager binding, naming strategy selection.
  • Phase 2 — Lifecycle. Eloquent observers mirroring 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.
  • Phase 3 — Dedupe and GC. Reference counting (a file_references join or a ref_count column) before enabling deletion; a scheduled orphan sweep.
  • Phase 4 — Serving. Signed URLs / CDN. Skip 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.

Operational Impact

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.

  • Metadata lives in the primary database. At high upload volume the 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.
  • MD5 computation on upload is CPU/IO-bound on the request path. Hash the stream incrementally; for large files, consider an async job or reject over a size threshold.
  • Object storage scales cleanly through whichever Flysystem adapter you choose — that is the package's strongest architectural decision.
  • Serving through a controller (as the README demonstrates) couples file throughput to PHP-FPM workers. For anything beyond internal tooling, use signed URLs or a CDN. Do not ship the controller-based download path to production.

Failure modes to design against explicitly:

  1. Committed row, missing object. The README's lifecycle moves the file in 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.
  2. Rollback orphans. Listener fires, object moves to final location, transaction rolls back → unreferenced blob with no metadata row. Mitigation: orphan sweep keyed on naming strategy.
  3. Unsafe deletion under dedupe. 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.
  4. Multi-node inconsistency with local adapters. A local filesystem adapter behind more than one app node produces divergent state. Require shared storage (S3/GCS) or enforce single-node.
  5. Naming strategy changes are breaking changes. Switching strategies makes existing objects unresolvable unless paths are persisted (PersistentPathStrategy) or a migration maps old→new keys. Treat naming strategy as a schema-level commitment.
  6. Tenant leakage. Global dedupe means tenant A can infer or reach tenant B's content by hash. Scope the hash namespace per tenant, or disable dedupe.
  7. Hash collision. MD5 collisions are constructible. For dedupe-by-content, an attacker-supplied collision yields content substitution. Low likelihood, non-zero impact; relevant if untrusted uploads are in scope.

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.

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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
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