Claude Code Plugins

Community-maintained marketplace

Feedback
0
0

Manage media files (images, PDFs, videos) in Laravel using Spatie's Media Library. Handle file uploads, store files on any filesystem (S3, local), generate image conversions (thumbnails, responsive images), manage file metadata with custom properties, and serve files with automatic URL generation. Use when implementing file upload functionality, image processing, media collections, responsive images, or file management in Laravel applications.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name laravel-medialibrary
description Manage media files (images, PDFs, videos) in Laravel using Spatie's Media Library. Handle file uploads, store files on any filesystem (S3, local), generate image conversions (thumbnails, responsive images), manage file metadata with custom properties, and serve files with automatic URL generation. Use when implementing file upload functionality, image processing, media collections, responsive images, or file management in Laravel applications.

Laravel Media Library

Comprehensive file management package for Laravel that associates files with Eloquent models. Handles uploads, storage on any filesystem, image conversions, metadata, and responsive images.

Quick Start

1. Install and Setup

composer require spatie/laravel-medialibrary
php artisan migrate
php artisan vendor:publish --provider="Spatie\MediaLibrary\MediaLibraryServiceProvider" --tag="config"

2. Add to Model

use Spatie\MediaLibrary\HasMedia;
use Spatie\MediaLibrary\InteractsWithMedia;

class Article extends Model implements HasMedia
{
    use InteractsWithMedia;
    
    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('images')->singleFile();
        $this->addMediaCollection('gallery');
    }
    
    public function registerMediaConversions(?Media $media = null): void
    {
        $this->addMediaConversion('thumb')
            ->width(150)
            ->height(150);
    }
}

3. Upload Files

// From request
$article->addMedia($request->file('image'))
    ->toMediaCollection('images');

// From URL
$article->addMediaFromUrl('https://example.com/photo.jpg')
    ->toMediaCollection('gallery');

// From base64
$article->addMediaFromBase64($base64String)
    ->toMediaCollection('images');

4. Get Media

// Get media URLs
$article->getFirstMediaUrl('images'); // original
$article->getFirstMediaUrl('images', 'thumb'); // conversion

// Get media items
$media = $article->getMedia('gallery');
$firstImage = $article->getFirstMedia('images');

// Check if has media
if ($article->hasMedia('gallery')) {
    // do something
}

Collections & Storage

Define Collections with Validation

public function registerMediaCollections(): void
{
    // Single file collection
    $this->addMediaCollection('avatar')
        ->singleFile()
        ->acceptsMimeTypes(['image/jpeg', 'image/png']);
    
    // Keep only 5 latest files
    $this->addMediaCollection('photos')
        ->onlyKeepLatest(5);
    
    // Custom disk (S3)
    $this->addMediaCollection('videos')
        ->useDisk('s3');
    
    // With fallback image
    $this->addMediaCollection('thumbnail')
        ->useFallbackUrl('/images/default.png');
    
    // Custom validation
    $this->addMediaCollection('documents')
        ->acceptsFile(function ($file) {
            return $file->size < 5000000;
        })
        ->acceptsMimeTypes(['application/pdf']);
}

Specify Disk During Upload

$article->addMedia($request->file('video'))
    ->toMediaCollection('videos', 's3'); // s3 disk

Image Conversions

Define Conversions

public function registerMediaConversions(?Media $media = null): void
{
    // Basic resize
    $this->addMediaConversion('thumb')
        ->width(150)
        ->height(150);
    
    // Crop and format
    $this->addMediaConversion('hero')
        ->crop(1200, 600)
        ->format('webp')
        ->quality(90);
    
    // Collection-specific
    $this->addMediaConversion('preview')
        ->performOnCollections('gallery', 'images');
    
    // Keep original format
    $this->addMediaConversion('large')
        ->width(1920)
        ->keepOriginalImageFormat();
    
    // Non-queued (sync)
    $this->addMediaConversion('sync-thumb')
        ->width(100)
        ->nonQueued();
}

Regenerate Conversions

# All conversions
php artisan media-library:regenerate

# For specific model
php artisan media-library:regenerate --model="App\Models\Article"

# Specific IDs
php artisan media-library:regenerate --ids=1,2,3 --model="App\Models\Article"

# Only certain conversions
php artisan media-library:regenerate --only=thumb,large

Responsive Images

Enable Responsive Images

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

// In conversion
public function registerMediaConversions(?Media $media = null): void
{
    $this->addMediaConversion('hero')
        ->width(1920)
        ->withResponsiveImages();
}

// During upload
$article->addMedia($request->file('image'))
    ->withResponsiveImages()
    ->toMediaCollection('images');

Get Responsive URLs

$media = $article->getFirstMedia('images');

// Get srcset attribute
$srcset = $media->getSrcset();
$heroSrcset = $media->getSrcset('hero');

// Check if available
if ($media->hasResponsiveImages()) {
    echo $media->getUrl();
}

Custom Properties & Metadata

Store and Retrieve Metadata

// Upload with custom properties
$article->addMedia($request->file('file'))
    ->withCustomProperties([
        'author' => 'John Doe',
        'tags' => ['important', 'archived'],
        'version' => '1.0'
    ])
    ->toMediaCollection('documents');

// Set/update properties
$media = $article->getFirstMedia('documents');
$media->setCustomProperty('reviewed', true)
    ->setCustomProperty('reviewer', 'Jane Smith')
    ->save();

// Get properties
$author = $media->getCustomProperty('author');
$default = $media->getCustomProperty('status', 'pending');

// Check exists
if ($media->hasCustomProperty('approved')) {
    // do something
}

// Remove property
$media->forgetCustomProperty('draft')->save();

Multiple Files & Batch Operations

Multiple Uploads

// Specific keys from request
$fileAdders = $article->addMultipleMediaFromRequest(['image1', 'image2']);
foreach ($fileAdders as $adder) {
    $adder->toMediaCollection('gallery');
}

// All files from request
$allAdders = $article->addAllMediaFromRequest();
foreach ($allAdders as $key => $adder) {
    $adder->toMediaCollection('uploads');
}

// Single file from request key
$article->addMediaFromRequest('featured_image')
    ->toMediaCollection('images');

Update & Delete Media

// Update media
$media = Media::find(1);
$media->name = 'New Name';
$media->setCustomProperty('status', 'active');
$media->save();

// Update collection
$newArray = [
    ['id' => 1, 'name' => 'First', 'custom_properties' => ['order' => 1]],
    ['id' => 2, 'name' => 'Second', 'custom_properties' => ['order' => 2]],
];
$article->updateMedia($newArray, 'gallery');

// Clear collection
$article->clearMediaCollection('gallery');

// Delete specific media
$article->deleteMedia(1); // by ID
$article->deleteMedia($media); // by object

// Move to another model
$newMedia = $media->move($product, 'photos');

// Copy to another model
$copied = $media->copy($product, 'photos');

Advanced Features

Filtering Media

// Filter by custom property
$featured = $article->getMedia('gallery', function ($media) {
    return $media->getCustomProperty('featured') === true;
});

// Filter by mime type
$pdfs = $article->getMedia('documents', function ($media) {
    return $media->mime_type === 'application/pdf';
});

// Filter by date
$recent = $article->getMedia('images', function ($media) {
    return $media->created_at->isAfter(now()->subDays(7));
});

Stream & Download

// Download response
Route::get('/media/{media}/download', function (Media $media) {
    return $media->toResponse(request());
});

// Inline display
Route::get('/media/{media}/view', function (Media $media) {
    return $media->toInlineResponse(request());
});

// Stream with custom chunk size
Route::get('/media/{media}/stream', function (Media $media) {
    return $media
        ->setStreamChunkSize(2 * 1024 * 1024)
        ->toResponse(request());
});

ZIP Downloads

use Spatie\MediaLibrary\Support\MediaStream;

// Single collection
Route::get('/article/{article}/photos.zip', function (Article $article) {
    return MediaStream::create('photos.zip')
        ->addMedia($article->getMedia('photos'));
});

// Multiple collections
Route::get('/article/{article}/all.zip', function (Article $article) {
    return MediaStream::create('all.zip')
        ->addMedia($article->getMedia('images'))
        ->addMedia($article->getMedia('documents'));
});

Email Attachments

class InvoiceMail extends Mailable
{
    public function build()
    {
        return $this->view('emails.invoice')
            ->attach($this->article->getFirstMedia('invoices')->toMailAttachment());
    }
}

// With conversion
$mail->attach($media->mailAttachment('pdf-preview'));

Render as HTML

$media = $article->getFirstMedia('images');

// Basic img tag
echo $media->img();

// With conversion
echo $media->img('thumb');

// With attributes
echo $media->img('thumb', ['class' => 'thumbnail', 'loading' => 'lazy']);

// In Blade
{!! $media->img('thumb', ['class' => 'img-fluid']) !!}

Configuration & Customization

config/media-library.php

return [
    'disk_name' => env('MEDIA_DISK', 's3'), // S3 by default
    'queue_conversions_by_default' => true, // Queue all conversions
    'queue_name' => 'media-conversions',
    'temporary_url_default_lifetime' => 60, // 1 hour temp URLs
    'path_generator' => CustomPathGenerator::class,
    'url_generator' => CustomUrlGenerator::class,
    'file_namer' => CustomFileNamer::class,
];

Custom Path Generator

use Spatie\MediaLibrary\Support\PathGenerator\PathGenerator;

class CustomPathGenerator implements PathGenerator
{
    public function getPath(Media $media): string
    {
        return $media->model_type . '/' . $media->model_id . '/';
    }
    
    public function getPathForConversions(Media $media): string
    {
        return $this->getPath($media) . 'conversions/';
    }
}

Custom URL Generator

use Spatie\MediaLibrary\Support\UrlGenerator\BaseUrlGenerator;

class CustomUrlGenerator extends BaseUrlGenerator
{
    public function getUrl(): string
    {
        return 'https://cdn.example.com/' . $this->getPathRelativeToRoot();
    }
}

Common Patterns

Model with All Features

class Product extends Model implements HasMedia
{
    use InteractsWithMedia;
    
    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('thumbnail')
            ->singleFile()
            ->acceptsMimeTypes(['image/jpeg', 'image/png']);
        
        $this->addMediaCollection('gallery')
            ->onlyKeepLatest(10)
            ->withResponsiveImages();
        
        $this->addMediaCollection('manuals')
            ->acceptsMimeTypes(['application/pdf']);
    }
    
    public function registerMediaConversions(?Media $media = null): void
    {
        $this->addMediaConversion('thumb')
            ->width(150)
            ->height(150);
        
        $this->addMediaConversion('hero')
            ->width(1200)
            ->height(600)
            ->withResponsiveImages()
            ->performOnCollections('gallery');
    }
}

Upload & Redirect

class ProductController extends Controller
{
    public function store(Request $request)
    {
        $product = Product::create($request->validated());
        
        if ($request->hasFile('thumbnail')) {
            $product->addMedia($request->file('thumbnail'))
                ->toMediaCollection('thumbnail');
        }
        
        if ($request->hasFile('images')) {
            foreach ($request->file('images') as $image) {
                $product->addMedia($image)
                    ->toMediaCollection('gallery');
            }
        }
        
        return redirect()->route('products.show', $product);
    }
}

Display Media

@if($product->hasMedia('thumbnail'))
    <img src="{{ $product->getFirstMediaUrl('thumbnail', 'thumb') }}" 
         srcset="{{ $product->getFirstMedia('thumbnail')->getSrcset() }}"
         alt="{{ $product->name }}"
         loading="lazy">
@endif

@foreach($product->getMedia('gallery') as $photo)
    <img src="{{ $photo->getUrl('hero') }}"
         srcset="{{ $photo->getSrcset('hero') }}"
         alt="{{ $photo->name }}">
@endforeach

Best Practices

  • Use collections to organize different media types (thumbnail, gallery, documents)
  • Define conversions in registerMediaConversions() for consistent image processing
  • Enable responsive images for better mobile performance
  • Use custom properties to store metadata (featured, approved, author, etc.)
  • Queue conversions for large files to avoid blocking requests
  • Use disk configuration to store on S3 for production
  • Filter media programmatically using callbacks for complex queries
  • Add custom validation in collections to enforce rules
  • Use temporary URLs for private files (S3 signed URLs)
  • Implement soft deletes on media when needed

For advanced usage, see reference.md.