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

Nova Media Library Field Laravel Package

jameslkingsley/nova-media-library-field

A Laravel Nova field for managing media library attachments, letting you upload, browse, and select existing media on your models. It integrates with Spatie's Laravel Media Library for a smooth image and file picking experience in the Nova admin panel.

View on GitHub
Deep Wiki
Context7

Getting Started

Before anything else: check compatibility. This package's last release was January 2019 and it is archived. That places it in the Nova 1.x/2.x + spatie/laravel-medialibrary v7/v8 era. Open composer.json in the repo and compare its nova and laravel-medialibrary constraints against your app before you composer require — on Nova 4+ / medialibrary v10+ nothing will work without a fork.

  1. Install the package alongside Spatie Media Library:

    composer require jameslkingsley/nova-media-library-field
    

    Nova auto-discovers the service provider; if not, register it in config/app.php.

  2. Prepare the model (this is table stakes for Spatie, and the field adds nothing here):

    class Post extends Model implements HasMedia
    {
        use InteractsWithMedia;
    
        public function registerMediaCollections(): void
        {
            $this->addMediaCollection('cover')->singleFile();
            $this->addMediaCollection('gallery');
        }
    }
    
  3. Publish/migrate Spatie's media table and link storage:

    php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="migrations"
    php artisan migrate
    php artisan storage:link
    
  4. Add the field to your Nova resource:

    public function fields(Request $request)
    {
        return [
            MediaLibrary::make('Cover', 'cover'),
        ];
    }
    

    Where to look first: the package README is thin, so read src/ — the field class, its resolve()/fill() methods, and its Vue component. That tells you the exact class namespace, constructor signature (label, collection), and which fluent methods (single(), multiple(), accept(), disk()) actually exist. Do not guess method names from other media fields; they are not interchangeable.

  5. First use case: a single cover image on a Post resource. Create a post with an image, confirm a row appears in media with collection_name = 'cover', then edit the post and replace the image.

Implementation Patterns

One field per collection. The field maps 1:1 to a Spatie collection name. A model with cover and gallery gets two fields; there is no combined "all media" field.

Keep single/multiple in sync with the collection definition. The field's mode and addMediaCollection()->singleFile() must agree. A single() field on a non-single collection leaves orphaned media; a multiple() field on a singleFile() collection silently keeps only the last upload.

Create forms are the fragile path. The model doesn't exist when the file is uploaded, so the field must stash the upload and attach it after the model is saved. If you see uploads vanish on create but work fine on update:

  • Inspect the field's fill()/resolve() to see where it stashes the temp path.
  • Fall back to an explicit hook in the model or an observer:
    public function registerMediaCollections(): void { /* ... */ }
    
    // In a PostObserver::created() or Model::created()
    if ($path = request('cover_temp_path')) {
        $post->addMedia($path)->toMediaCollection('cover');
    }
    
  • Or wrap the flow in a custom FormRequest/Resource::store().

Validation. Prefer validating in the resource's rules() / a FormRequest ('cover' => ['image', 'max:2048']) over the field's own ->rules(). Nova validates before save, so the input is a temp upload — validate the upload, not the media record.

Thumbnails and conversions. Define conversions in registerMediaConversions() and point the field at one if it supports ->conversion(); otherwise Nova previews load the original file. For galleries this is the difference between a snappy form and a 40 MB page.

Index and detail views. Media is not a column, so the field is form-only in practice. For a column in index, add an accessor:

public function getCoverUrlAttribute(): ?string
{
    return $this->getFirstMediaUrl('cover', 'thumb') ?: null;
}

Then use Image::make(fn () => $this->cover_url) — and eager-load with('media') in indexQuery to avoid N+1.

Metadata (alt text, captions). The field manages files, not custom properties. Pair it with a text field and persist in an updating observer on the media model (config('media-library.media_model')), or override the field's fill() in a subclass.

Never treat the field value as a model attribute. Don't read it back from $model->cover, don't ->sortable() it, don't ->resolveUsing(). Always derive URLs via getFirstMediaUrl() / getMedia().

Seeding and imports bypass Nova entirely. Use $model->addMedia($path)->toMediaCollection('cover') — the field is a UI, not an API. This also means data migrations are safe.

Gotchas and Tips

  • Archived since 2019. Pin your other dependencies around it, or budget a fork. Nova's field API (fields(), resolve, fill, meta) changed materially after v2; expect the Vue component and possibly the PHP field to break on Nova 3+.
  • 404 thumbnails are almost always environment, not code: missing php artisan storage:link, APP_URL mismatch behind a proxy, or a disk mismatch between config('nova.storage_disk'), filesystems.disks, and config('media-library.disk_name'). On S3 you need temporary URLs, not url().
  • Deletes are immediate and destructive. Removing media in the form deletes the record (and, depending on Spatie's config, the file). Gate it with ->canSee(fn () => $request->user()->can('update', $this)) for roles that shouldn't destroy assets.
  • Filename collisions. Spatie suffixes duplicates; never key anything off file_name. Use the media id or uuid.
  • Silent upload failures. Nova collapses backend errors into a generic toast. When uploads "do nothing," check storage/logs/laravel.log, then insert a dd() in the field's upload/store handler to confirm the request even reaches it.
  • ->rules('required') on create is a trap when the field's value is a temp path — the required rule can pass while the attach step fails. Test the create path specifically, not just update.
  • Debug from the database, not the UI. In tinker: $post->getMedia('cover'), and check media.model_type / collection_name / disk directly. Nine times out of ten the row is there with the wrong collection or disk.
  • Testing: Storage::fake('public') + UploadedFile::fake()->image('cover.jpg'), and drive Nova via Nova::actingAs($user); assert on $post->getMedia('cover')->count() rather than on rendered markup.
  • Extension point worth knowing: subclass the field and override resolve()/fill() to attach custom properties (alt text, credits) — cheaper than a parallel metadata model.
  • Have an exit plan. For new work, ebess/advanced-nova-media-library (actively maintained, similar feature set) or a plain Image/File field plus addMedia(...)->toMediaCollection(...) in Resource::store()/update() gives you the same result with no archived dependency.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib