crumbls/filament-media-library

A WordPress-like media library for Filament with central media pool and polymorphic attachments.

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/crumbls/filament-media-library

1.0.0 2026-02-07 05:09 UTC

This package is auto-updated.

Last update: 2026-02-07 05:21:23 UTC


README

A WordPress-style media library for Filament 5. Provides a central media pool with polymorphic attachments, so any model in your application can reference shared media through a clean pivot table.

Built on top of Spatie Media Library for file handling and image conversions.

Requirements

  • PHP 8.2+
  • Laravel 12+
  • Filament 5+
  • Spatie Laravel Media Library 11+

Installation

composer require crumbls/filament-media-library

The package auto-discovers its service provider. Migrations run automatically -- no publishing required.

Run the migrations:

php artisan migrate

Register the plugin in your Filament panel provider:

use Crumbls\FilamentMediaLibrary\FilamentMediaLibraryPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        // ...
        ->plugins([
            FilamentMediaLibraryPlugin::make(),
        ]);
}

Configuration

Publish the config file to customize settings:

php artisan vendor:publish --tag=filament-media-library-config
// config/filament-media-library.php
return [
    // Storage disk (any disk defined in config/filesystems.php)
    'disk' => env('MEDIA_LIBRARY_DISK', 'public'),

    // Allowed upload types (glob-style MIME matching)
    'accepted_file_types' => ['image/*', 'video/*', 'application/pdf'],

    // Max upload size in kilobytes
    'max_file_size' => 10240,

    // Automatic image conversions
    'image_conversions' => [
        'thumbnail' => ['width' => 150, 'height' => 150],
        'medium'    => ['width' => 300, 'height' => 300],
        'large'     => ['width' => 1024, 'height' => 1024],
    ],

    // Named collections (for future use)
    'collections' => [],

    // Swap the Media model with your own
    'models' => [
        'media' => \Crumbls\FilamentMediaLibrary\Models\Media::class,
    ],

    // Filament navigation
    'filament' => [
        'navigation_group' => null,
        'navigation_icon'  => 'heroicon-o-photo',
        'navigation_sort'  => null,
        'navigation_label' => 'Media Library',
    ],
];

Usage

Media Library Page

Once the plugin is registered, a "Media Library" page appears in your Filament panel navigation. It provides:

  • Grid view of all uploaded media with thumbnails
  • Drag-and-drop file uploads
  • Search across title, alt text, caption, and description
  • Type filtering (images, videos, documents)
  • Detail modal with editable metadata (title, alt text, caption, description)
  • Inline image editing (crop, rotate, flip) via CropperJS
  • File URL display for easy copying

Attaching Media to Models

Add the HasMediaLibrary trait to any Eloquent model:

use Crumbls\FilamentMediaLibrary\Traits\HasMediaLibrary;

class Post extends Model
{
    use HasMediaLibrary;
}

This gives you a full API for managing media attachments:

$post = Post::find(1);

// Attach a single media item
$post->attachMedia($mediaId);

// Attach with a named collection
$post->attachMedia($mediaId, 'featured-image');

// Attach with explicit order
$post->attachMedia($mediaId, 'gallery', order: 3);

// Attach multiple at once (order is auto-incremented)
$post->attachMediaMany([$mediaId1, $mediaId2, $mediaId3]);

// Attach multiple to a specific collection
$post->attachMediaMany([$mediaId1, $mediaId2], 'gallery');

// Detach media
$post->detachMedia($mediaId);

// Detach from a specific collection only
$post->detachMedia($mediaId, 'gallery');

// Sync a collection (replaces existing attachments)
$post->syncMedia([$mediaId1, $mediaId2], 'gallery');

// Query media in a collection
$galleryItems = $post->mediaInCollection('gallery')->get();

// Get all media (default collection)
$media = $post->getMediaByCollection();

// Access the relationship directly
$post->mediaLibrary; // MorphToMany, ordered by pivot 'order'

MediaPicker Form Component

Use the MediaPicker in any Filament form to let users select media from the library:

use Crumbls\FilamentMediaLibrary\Forms\Components\MediaPicker;

public static function form(Schema $schema): Schema
{
    return $schema->components([
        MediaPicker::make('featured_image_id'),
    ]);
}

Multiple selection:

MediaPicker::make('gallery_ids')
    ->multiple(),

Limit selections:

MediaPicker::make('gallery_ids')
    ->multiple()
    ->maxItems(5),

Named collection:

MediaPicker::make('hero_image_id')
    ->collection('hero'),

The picker opens a modal with search, pagination, and thumbnail previews. Selected media is stored as an ID (single) or array of IDs (multiple) on the model.

MediaColumn Table Column

Display media thumbnails in Filament tables:

use Crumbls\FilamentMediaLibrary\Tables\Columns\MediaColumn;

public static function table(Table $table): Table
{
    return $table->columns([
        MediaColumn::make('media')
            ->collection('featured-image')
            ->size(50)
            ->circular(),
    ]);
}

Options:

// Set thumbnail size in pixels (default: 40)
MediaColumn::make('media')->size(60)

// Circular thumbnail (default: false)
MediaColumn::make('media')->circular()

// Square thumbnail (default: true)
MediaColumn::make('media')->square(false)

// Specify collection (default: 'default')
MediaColumn::make('media')->collection('gallery')

Performance tip: Eager-load the media relation to avoid N+1 queries:

public static function table(Table $table): Table
{
    return $table
        ->modifyQueryUsing(fn ($query) => $query->with('mediaLibrary.media'))
        ->columns([
            MediaColumn::make('media'),
        ]);
}

Media Model

The Media model wraps Spatie Media Library and provides convenient accessors:

use Crumbls\FilamentMediaLibrary\Models\Media;

$media = Media::find(1);

$media->file_url;       // Full URL to the original file
$media->thumbnail_url;  // Thumbnail conversion URL (falls back to original)
$media->mime_type;      // e.g. 'image/jpeg'
$media->file_size;      // Size in bytes
$media->file_name;      // Stored file name

$media->isImage();      // true for image/* MIME types
$media->isVideo();      // true for video/* MIME types
$media->isPdf();        // true for application/pdf

Metadata fields:

Field Type Max Length
title string 255
alt_text string 255
caption text 1000
description text 5000

The model uses soft deletes. Each record gets an auto-generated UUID and tracks uploaded_by (foreign key to users).

Database Schema

The package creates two tables:

media_library -- Central media records

Column Type
id bigint (PK)
uuid string (unique)
title string (nullable)
alt_text string (nullable)
caption text (nullable)
description text (nullable)
disk string (default: 'public')
uploaded_by foreign key to users (nullable)
created_at timestamp
updated_at timestamp
deleted_at timestamp (soft delete)

mediables -- Polymorphic pivot table

Column Type
id bigint (PK)
media_id foreign key to media_library
mediable_type string
mediable_id bigint
collection string (default: 'default')
order unsigned int (default: 0)
created_at timestamp
updated_at timestamp

Spatie Media Library also creates its own media table for the underlying file records.

Publishing Assets

# Config
php artisan vendor:publish --tag=filament-media-library-config

# Migrations (only if you need to customize them)
php artisan vendor:publish --tag=filament-media-library-migrations

# Views (for template customization)
php artisan vendor:publish --tag=filament-media-library-views

Storage Disk

Set the storage disk via environment variable or config:

MEDIA_LIBRARY_DISK=s3

Any disk defined in config/filesystems.php works -- public, s3, r2, or a custom disk.

Extending the Media Model

Swap in your own Media model by updating the config:

// config/filament-media-library.php
'models' => [
    'media' => App\Models\CustomMedia::class,
],

Your custom model should extend the package's Media model:

namespace App\Models;

use Crumbls\FilamentMediaLibrary\Models\Media;

class CustomMedia extends Media
{
    // Add custom methods, scopes, or relationships
}

Security

Uploads are validated server-side:

  • MIME type validation -- Files are checked against accepted_file_types using glob-style matching (e.g., image/* matches image/png). Rejected files are not stored.
  • File size limits -- Enforced against max_file_size config value.
  • Filename sanitization -- Uploaded filenames are slugified to strip path traversal sequences, special characters, and double extensions.
  • Metadata validation -- Title and alt text are limited to 255 characters, caption to 1000, description to 5000.

Rejected uploads dispatch an fml-uploads-rejected Livewire event with details for frontend notification.

Development

Code Style

This project uses Laravel Pint for code formatting. A pre-push git hook runs Pint automatically and blocks pushes with style violations.

Run Pint manually:

vendor/bin/pint src config database tests

Static Analysis

Larastan is configured at level 5:

vendor/bin/phpstan analyse --configuration=packages/filament-media-library/phpstan.neon

Testing

The package uses Pest with 179 tests:

vendor/bin/pest --test-directory packages/filament-media-library/tests --configuration packages/filament-media-library/phpunit.xml

License

MIT. See LICENSE.