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

Media Laravel Package

nuewire/media

Nuewire Media provides a simple Laravel media library for uploading, storing, and attaching files to Eloquent models, with reusable conversions, clean storage integration, and an easy API for managing images and documents in your applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and publish. Two commands get you to a working state:

composer require nuewire/media
php artisan vendor:publish --tag=media-config
php artisan vendor:publish --tag=media-migrations
php artisan migrate

Pin the version in composer.json ("nuewire/media": "^1.0.1") rather than floating — you are on a 1.0.x line with a thin release history, and you want upgrades to be deliberate.

Where to look first, in this order:

  1. config/media.php — the storage disk, path/filename strategy, conversion defaults, and queue toggle. Nearly every "why is my file here / not here" question resolves in this file.
  2. The HasMedia-style trait — this is what you add to models and what defines the collection API surface (addMedia*, toMediaCollection, getMedia, clearMediaCollection).
  3. The media model itself — your read path for URLs, conversions, and metadata.
  4. The service provider — only if you need to swap a path generator or URL generator.

First use case — attach an upload and render it. Add the trait to a model, define a collection, then:

class Post extends Model
{
    use HasMedia;

    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('images');
    }

    public function registerMediaConversions(Media $media = null): void
    {
        $this->addMediaConversion('thumb')->width(320)->height(320);
    }
}
$media = $post->addMediaFromRequest('image')->toMediaCollection('images');

echo $media->getUrl();        // original
echo $media->getUrl('thumb'); // conversion

Smoke test before you build anything on top: upload one file against a throwaway model, confirm a row lands in the media table, confirm the file exists on the configured disk, and confirm both URLs 200. If that passes, the disk and path strategy are correct and everything after this is application design.

Implementation Patterns

Put collection and conversion definitions on the model, not in controllers. Registering collections in one place is what makes cleanup, regeneration, and URL generation predictable later. Descriptive names (avatars, gallery, documents) beat default and files.

Always upload through the request/disk helpers. addMediaFromRequest(), addMediaFromDisk(), and addMediaFromString() handle naming, MIME detection, and disk placement consistently. Hand-constructing media rows is where you get duplicate files, wrong MIME types, and orphans.

Distinguish single-file from multi-file collections deliberately. Avatars and hero images are single-file collections (new upload replaces old, one getFirstMediaUrl() call, no accumulation). Galleries are multi-file. Getting this wrong at the schema level is expensive to fix — an avatar collection that silently accumulates ten files will produce nondeterministic getFirstMediaUrl() output.

Queue conversions in production, always. Synchronous image resizing inside the request cycle is the classic media-library latency cliff. Enable the queue driver in config/media.php, run a worker, and keep synchronous conversion for tests and seeders only. Then make sure variant URLs are not requested before the job finishes — either generate on upload for critical sizes, or ship a placeholder.

Be explicit about disk and visibility. Use a dedicated disk rather than the app's default. Public-rendered images go on the public disk; contracts, IDs, and anything PII goes on a private disk with signed/temporary URLs. Decide this per collection, not globally, and encode it in the collection registration.

Render URLs from the media model, never from storage paths in Blade. $post->getFirstMediaUrl('images', 'thumb') means a CDN or disk migration is a config change, not a grep across templates. Provide a fallback URL for the empty case or Blade will emit broken images on every fresh record.

Eager-load media on index pages. Any listing that touches getFirstMediaUrl() per row is an N+1 waiting to happen. Eager-load the media relation (and filter to the collection you need) at the query level.

Wire maintenance into the scheduler from day one:

$schedule->command('media:clean')->weekly();
$schedule->command('media:regenerate')->weekly();

Orphaned files and stale conversions are the long-term failure mode of every media library; the fix is cheap if it runs on a schedule and painful if it runs never.

Test against Storage::fake() and seed through the real API. Calling addMediaFromString() in a factory exercises naming, MIME detection, and conversation registration — the parts that break. Asserting on a hand-inserted media row tests nothing.

Gotchas and Tips

Read the 1.0.0→1.0.1 diff before you trust the release. The notes are an autogenerated compare link with no content. For a young package touching path generation and storage, a patch bump can still change behaviour your URLs depend on. Same discipline on every future patch: read the diff, run the suite against a staging copy where media URLs are live.

Deploy scripts must restart the queue. A stale worker after deploy means conversions run against the old code — or silently never run. queue:restart belongs next to migrate in your release script. Missing variants present as 404s, not as errors, so this failure is easy to miss for days.

Disk and path changes do not move existing files. The package resolves storage at generation time. Switch disks or restructure paths and old media keeps pointing at the old location. Plan it as a migration: regenerate with the new config, copy or rewrite the underlying objects, then flip.

URLs get cached outside the package. Model attributes, response caches, and your CDN all hold URLs the package cannot invalidate. Treat a disk or domain change as a cache-busting event and purge deliberately.

Verify cascade behaviour yourself. Whether deleting a parent model deletes its media rows and files depends on how the trait hooks into model events. Test it explicitly — then add a deleting observer or a DB cascade if you need hard cleanup. Silently orphaned files are the norm, not the exception.

Validation is entirely your responsibility. The package stores what you hand it. MIME type, extension, and size limits belong in a Form Request before the upload call. Never pass raw user filenames into a custom naming strategy — keep whatever uniqueness guarantee the default strategy provides, or you will get overwrites on the same second-granularity timestamp.

Replacing media is an explicit call, not a side effect. For single-file collections use the package's replace path (or clearMediaCollection() then add); do not assume a second upload overwrites the first unless your collection is registered as single-file.

Debug broken images in this order: configured disk → resolved path from the media model → file exists on disk → URL generation → queue worker status. Nine times out of ten it is a disk or path mismatch, not a package bug. Log the resolved path once and the class of problem usually identifies itself.

Extension points, not edits. Custom path generators, custom URL generators, and per-conversion definitions are the intended seams. Overriding package internals will conflict with the next patch release, and this package is releasing patches.

Upgrade discipline for a 0-star dependency. Keep a small integration test that uploads, converts, and asserts a URL resolves. That test is your only real protection against a silent patch-level regression, and it costs twenty lines.

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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo