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

Laravel Mediable Laravel Package

plank/laravel-mediable

Laravel-Mediable lets you easily upload files and attach them to Eloquent models via many-to-many polymorphic relationships, with tags for organizing media, configurable filesystem destinations, MIME/extension restrictions, and image variant support.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. plank/laravel-mediable is a conventional Laravel package (service provider, facade, publishable config, package-loaded migrations) and slots into the framework the same way first-party components do. Its data model is the main architectural decision it forces on you: a media table plus a mediables polymorphic many-to-many pivot carrying a tag column. That means no schema changes to your domain models, at the cost of introducing a generic attachment table that becomes a shared dependency across the app. For a product with heterogeneous attachable entities (posts, users, products, CMS pages) this is a good trade. For a system with one or two media-bearing models and strict per-domain storage rules, the abstraction may be more generality than the product needs.

The package is filesystem-driven on Laravel's Flysystem disks, with pluggable source adapters (uploaded file, remote URL, stream, raw content, data URL as of 6.0) and optional image manipulation via intervention/image. That is a clean separation: storage concerns live in config/filesystems.php, upload semantics in MediaUploader, and relationships in the model trait. Variant generation is expressed as queued jobs — correct for the problem, but it pulls your queue infrastructure into the feature's critical path.

Integration feasibility. Low-friction. composer require, auto-discovery on Laravel 5.5+, one vendor:publish, one migrate. The 6.0 line requires PHP 8.1+ (dropped 7.4/8.0) and Laravel 10/11 (dropped 8/9), and adds intervention/image 3.x support via intervention/image-laravel. A code spike should be a day's work, not a sprint.

Technical risk.

  • Vendor concentration. Maintained by a single agency (Plank Design), with 828 stars and effectively no dependents signal on Packagist. The changelog shows genuine engineering rigor (PHPStan in CI, streaming source adapters, migration-squashing fixes, S3 temp URL fixes), but there is no commercial support tier and no bus factor. Assume you may need to fork and patch.
  • Version window narrowness. The project tracks Laravel majors with a hard drop policy: 4.4 dropped Laravel <6, 5.4 dropped 6/7, 6.0 dropped 8/9. Every Laravel upgrade is therefore a coupled package upgrade, often a major-version one with breaking changes (6.0 rewrote every SourceAdapter, removed getStreamResource(), getContents(), path(), valid(), and renamed ImageManipulation::usingHashForFilename()). Budget upgrade work per Laravel major — this is not a fire-and-forget dependency.
  • Dual-write consistency. The package cannot make filesystem and database writes atomic. An upload that lands on disk but fails to persist a Media row leaves an orphan file; a deleted model does not delete files unless you explicitly do so. Orphans are the primary long-term cost driver.
  • Breaking-change surface in 6.0 specifically. Alt attributes and image-manipulation-on-upload are new columns and behaviors; if we build against 6.x we should pin the minor and read UPGRADING.md before jumping, since migrations are now loaded from the package (a 5.0 change) rather than copied into database/migrations.
  • Data-quality caveat. Package metadata reports a last release date of 2026-10-12 alongside release 6.0.3 — an inconsistency worth re-verifying against Packagist before we commit to a support contract internally.

Key questions.

  1. Which Laravel and PHP versions are we standardizing on, and how many majors of headroom does the 6.x line give us?
  2. Do we need image variants (thumbnails, responsive sizes)? If yes, is async-only acceptable, or do we need synchronous generation for admin UX?
  3. What is the serving path — public disk behind the web server/CDN, or signed/temporary URLs (S3) proxied through the app? This drives both security posture and CDN cost.
  4. Multi-disk or multi-tenant? Can one media table serve tenant-isolated buckets, or does isolation require a custom Media subclass and table (a path the package supports but that has historically been bug-prone — see 4.4.2, 5.0.6)?
  5. Do we need EXIF stripping, virus scanning, or content-hash deduplication? Only validateHash() and useHashForFilename() are in-box; the rest is ours to build.
  6. Who consumes alt text — is it authored per-locale, and does that belong in media or in our domain tables?

Integration Approach

Stack fit. Strong fit for a standard Laravel app on PHP 8.1+ / Laravel 10–11, Flysystem 3 disks, and a queue worker pool. It composes with intervention/image 3.x and spatie/image-optimizer for derivative and optimization work. It does not fit apps that need per-domain attachment tables, hard real-time variant generation, or non-Laravel services that must read media metadata directly.

Migration path. For a greenfield feature, the path is: install and pin the 6.x minor, publish config/mediable.php, define upload disks with explicit visibility, run the package migrations, and wrap the facade behind an internal MediaService. For brownfield, the harder question is backfill: existing files need Media rows plus mediables pivot entries, and existing URLs need to be rewritten or redirected. Scope that as its own workstream — it is typically larger than the package integration itself.

Compatibility. Verify before committing: PHP ≥8.1, Laravel 10/11, intervention/image v3 pairing, and Flysystem minor constraints (historically a source of breakage — see 4.3.2 and 5.4.1). Because migrations ship inside the package, confirm no conflicts with a squashed migration history (5.0.0's stated reason for the change) and check that the app's doctrine/inflector version is compatible. Confirm S3 temp-URL generation respects disk root configuration on Flysystem 3 — that was patched in 5.4.1 but is exactly the kind of thing to assert in a smoke test.

Sequencing.

  1. Spike (1–3 days). Install in a scratch app; validate upload → attach → retrieve → variant generation → S3 temp URL end-to-end. Re-verify the release/version metadata.
  2. Contract freeze. Pin the major/minor, decide public-vs-signed serving, decide disk topology (single vs. per-environment vs. per-tenant), and write down the tag taxonomy (thumbnail, featured, gallery, download) before any model uses it. Tags are a de-facto schema; ad-hoc tags are the most common way this package becomes unmaintainable.
  3. Internal wrapper. Do not let MediaUploader and getMedia() calls spread through controllers. One service class owns upload policy (allowed MIME/extension/aggregate types, destination, visibility, alt text, hash policy), one owns retrieval and URL generation.
  4. First model integration. One entity, one tag, with tests covering duplicate handling, rejection of disallowed types, and orphan cleanup.
  5. Variants + queue. Enable only after worker capacity and retry/failure semantics are defined; a failed CreateImageVariants job leaves a media record with no variants and no user-visible error unless we surface it.
  6. Backfill and URL cutover, then hardening (cleanup job, monitoring, CDN).

Operational Impact

Maintenance. Expect periodic major-version work tied to Laravel upgrades rather than steady-state patching. The changelog is disciplined but the cadence is reactive to framework releases, so a Laravel upgrade plan must include a mediables line item. Because migrations live in the package now, upgrades can move the schema underneath us — every upgrade needs a migrate --pretend review. Owning a thin internal wrapper is the cheapest insurance against needing a fork.

Support. Community-only: GitHub issues, readthedocs, an UPGRADING.md. No SLA, no vendor escalation. For a media pipeline that is user-facing and revenue-adjacent, that argues for keeping the integration surface small enough that we can patch it ourselves, and for treating "fork and pin" as a legitimate, budgeted option rather than a failure mode.

Scaling. Storage scales trivially (S3 + CDN); the database does not. Watch for:

  • N+1 queries from getMedia() inside loops — eager-load the polymorphic relation.
  • Pivot indexing on (mediable_type, mediable_id, tag); 4.3.2 removed a redundant index, so verify our query plans against real cardinality.
  • Queue pressure from variant generation — one upload can fan out to many derivative jobs and many objects; batch and throttle.
  • The 6.0 source-adapter rewrite removed whole-file-in-memory reads for metadata, which materially improves large-file behavior, but image manipulation still loads contents into memory by design (5.0.5). Large images remain a memory ceiling.

Failure modes.

  • Orphaned files — write succeeds, DB insert fails, or parent model deleted without media cleanup. Needs a scheduled reconciliation job.
  • Orphaned records — file deleted out-of-band (lifecycle rule, manual S3 cleanup) while Media row persists; URLs 404 silently.
  • Variant jobs stuck or failed — no automatic user-visible signal; requires monitoring on failed jobs and a "variants missing" health check.
  • Filename collisions and sanitization surprises — 5.0.0 restricted names to URL-safe ASCII with transliteration, 5.5.0 made transliteration locale-dependent; identical uploads from different locales can diverge. Hash-based filenames avoid this if dedup or cache-busting matters.
  • Visibility misconfiguration — 6.0 changed the default to inherit disk visibility rather than assuming public; an upgrade or config drift can silently flip access on private assets.
  • Temp URL expiry — signed S3 URLs embedded in cached HTML break after expiry; never persist generated URLs.

Ramp-up. Low for anyone fluent in Laravel: half a day to productive, two to three days to confident with variants and S3. The real ramp-up cost is organizational, not technical — establishing the tag taxonomy, the upload policy, and the "no direct facade calls in controllers" convention. Without those, the package's flexibility becomes a source of inconsistency within a quarter. Budget roughly a sprint for integration plus a sprint for backfill and hardening on a mid-sized app.

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.
thuraaung2493/laravel-rulecraft
cyberwizard/schedule-catchup
livetennisapi/livetennisapi-laravel
roundly-consulting/notifications-for-laravel
flightsadmin/bootstrap-starter-kit
lasserafn/laravel-intempus
likyly/sdk
tdt/input
coreation/php-shapefile
coreation/gisconverter
primix/widgets
primix/notifications
primix/actions
primix/tables
primix/details
primix/forms
primix/support
marcelgwerder/laravel-api-handler
firebrikdotcom/conveyor-laravel-broadcaster
kanata-php/conveyor-laravel-broadcaster