roundly-consulting/media-library-for-laravel
A Laravel media library for attaching files to Eloquent models. Upload and store images or documents, generate conversions and responsive thumbnails, organize media in collections, and manage custom properties through a clean, fluent API. Supports local and cloud disks for flexible media management.
Architecture fit
This package sits directly on Laravel's Eloquent + Filesystem + Database transaction primitives. The 1.1.4 fixes confirm a design assumption that matters for architecture review: correctness depends on a transactional RDBMS with row-level locking (SELECT ... FOR UPDATE) and on application code actually wrapping media operations in transactions. The package is not a standalone service; it is a persistence-layer library whose invariants are shared between the media table, the storage disk, and the caller's transaction boundary.
The 1.3.1 factory fix adds a second architectural signal: the media model is a supported extension point. A host application can swap in its own model class, and the package's factories now resolve and build that configured class rather than a hardcoded base model. That moves "custom media model" from an undocumented trick to a tested configuration, which is relevant if we intend to attach domain behavior (policies, morph maps, custom casts) to the media entity.
Integration feasibility
The 1.3.1 change is confined to test-time code — factory model resolution — with no runtime API, schema, or config change implied. Integration cost is therefore low, with three caveats:
newModel() override, Factory::guessModelNamesUsing) may now double up or conflict.attach() and same-disk copy() can throw ModelNotFoundException when the shared row disappears before the lock is acquired.forceDelete() is deferred until transaction commit, changing timing assumptions for code that inspects the disk immediately after the call.Technical risk
Key questions
ModelNotFoundException from attach()/copy() documented as part of the public contract, and do we need a wrapper with app-specific semantics?Stack fit
Standard Laravel monolith or modular monolith with MySQL/PostgreSQL + a Laravel filesystem disk. Fits naturally if media operations already occur inside DB::transaction() blocks or inside queued jobs with afterCommit semantics. Fit degrades in architectures that share a storage bucket across multiple services writing to the same path space, since the package's locking only protects rows it owns. If we plan to subclass the media model, 1.3.1 makes that path more credible in tests, which is a mild point in favor of adopting it now rather than later.
Migration path
Compatibility
composer.json against our current runtime before scheduling; the constraint floor may have moved across 1.2/1.3.attach(), copy(), replace(), forceDelete(), and any custom deduplication configuration — these are where the 1.1.4-line behavior changes surface.Sequencing
attach(), copy(), replace(), forceDelete(), plus any custom deduplication configuration.ModelNotFoundException at those call sites before upgrading, so the upgrade does not introduce unhandled exceptions.Maintenance
Low ongoing maintenance. This release fixes a defect in test infrastructure rather than adding runtime features, so there is no new configuration to own and no operational surface added. The maintenance burden that does increase sits in the application layer: we own catching ModelNotFoundException, reasoning about transaction boundaries around media writes, and now also owning the media model override if we choose to use one. Document these invariants in the internal runbook so they survive team turnover.
Support
1.3.1 adds no new production failure mode, so the expected ticket shapes are unchanged: (a) unexpected ModelNotFoundException surfacing from attach/copy paths, and (b) reports of files still present after an apparent delete (correct behavior during an open transaction, or an orphan after a crash). Give the support team a diagnostic script that checks whether files exist on disk without a referencing row. The only new support surface is internal: developers confused by factories producing a different model class, which is a documentation fix, not a ticket category.
Scaling
Throughput on the media table becomes lock-bound rather than CPU-bound for concurrent writes to the same underlying file. Batch imports that deduplicate heavily against a small set of files will serialize more than before. Mitigations: spread imports across time or shards, cap worker concurrency per file key, keep transactions short, and avoid holding a transaction open across a network filesystem write. Read paths are unaffected. None of this is changed by 1.3.1.
Failure modes
ModelNotFoundException from attach() / copy() when the shared row is deleted mid-flight. Correct but must be handled; unmapped, it becomes a 500.Ramp-up
Small but real. Engineers need one focused briefing covering: media operations belong inside transactions; transactions containing media writes must be short; ModelNotFoundException is an expected outcome in some races, not a bug; file deletion is commit-time, not call-time; and media factories now build whichever model is configured, so do not hardcode the base class in tests. Plan roughly half a day of team enablement plus an hour of staging validation against the production database engine, and add the changelog review for 1.2.0/1.3.0 as a short pre-work item before the upgrade goes out.
How can I help you explore Laravel packages today?