npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@codingfactory/mediables-vue

v2.4.16

Published

Vue components for Laravel Mediables package with comprehensive video support

Readme

Laravel Mediables v2.0

A powerful, backend-focused Laravel media management package with a clean, trait-based API. Inspired by Spatie's Media Library but built for maximum flexibility and performance.

Latest Version on Packagist Total Downloads MIT Licensed

Upgrading from v1.x? Check out our comprehensive Upgrade Guide for a smooth migration path.

What's New in v2.0

Service-Based API

use Mediables\Facades\MediaService;

// Option 1: Two-step (store then attach)
$media = MediaService::storeMediaFile(
    $request->file('image'),
    'public',           // disk
    'products',         // collection
    ['alt' => 'Photo']  // custom properties
);
$product->attachMedia($media, 'images');

// Option 2: Combined method (recommended)
$media = MediaService::uploadAndAttachToModel(
    auth()->user(),         // actor
    $request->file('image'),
    $product,               // target model
    'images',               // collection
    ['alt' => 'Photo']      // properties
);

Key Features

  • Trait-Based Architecture - Simple, clean integration with any Eloquent model
  • Service-Based Upload - Flexible upload workflows via MediaService facade
  • Media Collections - Organize media with powerful collection definitions
  • Smart Image Processing - Automatic conversions, responsive images, and 46 PIXI.js filters
  • Cloud-First Design - Native S3, CDN, and signed URL support
  • Performance Optimized - Multi-layer caching and lazy loading
  • Backward Compatible - Smooth migration path from v1.x
  • Modular Frontend - Optional Vue components (Blade and Livewire coming soon)

Table of Contents

Installation

composer require mediables/mediables:^2.0

Publish and run migrations:

php artisan vendor:publish --tag=mediables-migrations
php artisan vendor:publish --tag=mediables-config
php artisan migrate

Optional Frontend Packages

# Vue 3 components (available now)
npm install @mediables/vue

# Blade components (coming soon)
# composer require mediables/blade-components

# Livewire components (coming soon)
# composer require mediables/livewire-components

Quick Start

1. Add Traits to Your Model

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Mediables\Traits\InteractsWithMediables;

class Product extends Model
{
    use InteractsWithMediables;

    public function registerMediaCollections(): void
    {
        $this->addMediaCollection('images')
            ->acceptsMimeTypes(['image/*'])
            ->maxNumberOfFiles(10);

        $this->addMediaCollection('documents')
            ->acceptsMimeTypes(['application/pdf'])
            ->singleFile();
    }
}

2. Upload and Attach Media

use Mediables\Facades\MediaService;

// Option 1: Two-step (store then attach)
$media = MediaService::storeMediaFile(
    $request->file('image'),
    'public',           // disk
    'products',         // collection
    ['alt' => 'Photo']  // custom properties
);
$product->attachMedia($media, 'images');

// Option 2: Combined method (recommended)
$media = MediaService::uploadAndAttachToModel(
    auth()->user(),         // actor
    $request->file('image'),
    $product,               // target model
    'images',               // collection
    ['alt' => 'Photo']      // properties
);

3. Retrieve and Display Media

// Get all media in collection
$images = $product->getMedia('images');

// Get first media
$mainImage = $product->getFirstMedia('images');

// Get URL (original)
$originalUrl = $mainImage->getUrl();

// Get conversion URL (use Media model method)
$thumbUrl = $mainImage->getUrl('thumb');

// Get first media URL directly (NO conversion param)
$firstUrl = $product->getFirstMediaUrl('images');

// Check if has media
if ($product->hasMedia('images')) {
    // ...
}

// Count media
$count = $product->getMedia('images')->count();

Defining Media Collections

Define what media your models accept:

public function registerMediaCollections(): void
{
    // Single avatar image
    $this->addMediaCollection('avatar')
        ->singleFile()
        ->acceptsMimeTypes(['image/jpeg', 'image/png']);

    // Product gallery with limits
    $this->addMediaCollection('gallery')
        ->acceptsMimeTypes(['image/*'])
        ->maxFileSize(5 * 1024 * 1024) // 5MB per file
        ->maxNumberOfFiles(20);

    // Documents with specific types
    $this->addMediaCollection('documents')
        ->acceptsMimeTypes(['application/pdf', 'application/msword'])
        ->maxNumberOfFiles(10);
}

Working with Media

Upload Workflow (Service)

use Mediables\Facades\MediaService;

// Option 1: Two-step (store then attach)
$media = MediaService::storeMediaFile(
    $request->file('image'),
    'public',           // disk
    'products',         // collection
    ['alt' => 'Photo']  // custom properties
);
$product->attachMedia($media, 'images');

// Option 2: Combined method (recommended)
$media = MediaService::uploadAndAttachToModel(
    auth()->user(),         // actor
    $request->file('image'),
    $product,               // target model
    'images',               // collection
    ['alt' => 'Photo']      // properties
);

Managing Collections

// Clear all media from collection
$product->clearMediaCollection('images');

// Attach existing media
$product->attachMedia($existingMedia, 'images');

// Detach media
$product->detachMedia($media, 'images');

Working with Conversions

use Mediables\Facades\MediaService;

// Get conversion URL
$thumbnailUrl = $media->getUrl('thumb');

// Get responsive images srcset
$srcset = $media->getSrcset();

// Generate conversions manually
MediaService::generateConversions($media);

Configuration

Simplified configuration in config/mediables.php:

return [
    // Storage
    'disk' => env('MEDIA_DISK', 'public'),
    'conversions_disk' => env('MEDIA_CONVERSIONS_DISK', null),

    // Processing
    'queue_connection' => env('MEDIA_QUEUE_CONNECTION', 'sync'),
    'conversions' => [
        'thumb' => ['width' => 150, 'height' => 150],
        'preview' => ['width' => 500, 'height' => 500],
    ],

    // Video Provider (currently only 'bunny' is supported)
    'video' => [
        'provider' => env('VIDEO_PROVIDER', 'bunny'),
        'bunny' => [/* ... */], // Bunny.net config
    ],

    // Validation
    'max_file_size' => 10 * 1024 * 1024, // 10MB
    'allowed_mimes' => 'jpeg,jpg,png,gif,webp,svg,pdf',

    // Features
    'generate_responsive_images' => true,
    'versioning' => false,
    'cdn_url' => env('MEDIA_CDN_URL'),
];

Environment Variables for Video Providers

To configure your video provider, set VIDEO_PROVIDER in your .env file. Currently only bunny is supported.

Bunny.net Configuration: When VIDEO_PROVIDER is set to bunny, the following environment variables are required:

BUNNY_VIDEO_LIBRARY_ID="your_bunny_video_library_id"
BUNNY_VIDEO_API_KEY="your_bunny_video_api_key"
BUNNY_VIDEO_HOSTNAME="video.bunnycdn.com" # Default is usually sufficient
BUNNY_VIDEO_CDN_HOSTNAME="your_library_id.b-cdn.net" # or play.bunnycdn.com
BUNNY_VIDEO_WEBHOOK_SECRET="your_webhook_secret" # Used for verifying webhooks

Image Processing

Automatic Conversions

Define conversions that run automatically:

'conversions' => [
    'thumb' => [
        'width' => 150,
        'height' => 150,
        'fit' => 'crop',
        'quality' => 80,
    ],
    'preview' => [
        'width' => 500,
        'height' => 500,
        'fit' => 'contain',
    ],
],

On-the-fly Manipulation

use Mediables\Services\Core\ImageProcessor;

$processor = app(ImageProcessor::class);

// Generate specific conversion
$processor->generateConversion($media, 'thumb', [
    'width' => 150,
    'height' => 150,
    'fit' => 'crop',
]);

Available Filters

The package includes 46 image filters:

use Mediables\Services\MediaManipulationService;

$manipulationService = app(MediaManipulationService::class);

// Submit a manipulation request (validated data from ManipulateMediaRequest)
$manipulationService->manipulate([
    'media_uuid' => $media->uuid,
    'manipulations' => [
        'filter' => 'ColorMatrix-1977',
        'brightness' => 10,
        'contrast' => 15,
    ],
]);

// Apply server-side filters to a media item
$processedUrl = $manipulationService->applyFilters($media, [
    ['name' => 'ColorMatrix-1977'],
]);

Responsive Images

Responsive image generation is controlled by the generate_responsive_images config option. When enabled, responsive variants are generated automatically during upload.

// Enable in configuration
'responsive_images' => [
    'enabled' => true,
    'widths' => [320, 640, 1024, 1920, 2560],
],

// Get srcset for HTML
$srcset = $media->getSrcset('preview');

// Use in Blade templates
<img src="{{ $media->getUrl() }}"
     srcset="{{ $media->getSrcset() }}"
     sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw">

Working with Variants

Media variants allow you to create processed versions of your media:

// Create a variant with specific processing
$variant = $originalMedia->createVariant([
    'manipulation' => 'cropped',
    'dimensions' => ['width' => 500, 'height' => 500],
    'filters' => ['brightness' => 10, 'contrast' => 15]
]);

// Check processing status
if ($variant->isVariantCompleted()) {
    $variantUrl = $variant->getUrl();
}

// Get all variants for media
$variants = $originalMedia->variants;

// Delete specific variant
$variant->delete();

Signed URLs for Private Files

// Generate signed URL (expires in 60 minutes by default)
$signedUrl = $media->getSignedUrl();

// Custom expiration time
$signedUrl = $media->getSignedUrl(120); // 2 hours

// Check if URL is still valid
if ($media->isSignedUrlValid($signedUrl)) {
    // URL is still valid
}

Batch Operations

For bulk operations, iterate over your media collection using the standard service methods:

use Mediables\Facades\MediaService;

// Bulk upload with loop
$uploadResults = [];
foreach ($files as $file) {
    $uploadResults[] = MediaService::storeMediaFile($file, 'public', 'products');
}

// Bulk delete with loop
foreach ($mediaItems as $media) {
    MediaService::deleteMedia($media);
}

// Generate conversions for multiple media
foreach ($mediaCollection as $media) {
    MediaService::generateConversions($media);
}

Event System

The package dispatches comprehensive events:

// Listen to events
Event::listen(\Mediables\Events\MediaUploaded::class, function ($event) {
    Log::info('Media uploaded: ' . $event->media->uuid);
});

// Available events:
// - MediaUploaded
// - MediaManipulated
// - MediaDeleted
// - MediaAttached
// - MediaDetached
// - ConversionGenerated

Frontend Components

Frontend components are now in separate, optional packages:

Vue 3 Components

npm install @mediables/vue
<template>
  <ModelMediaManager
    :model-type="'product'"
    :model-uuid="product.uuid"
    collection="images"
    :max-files="10"
    @upload-complete="handleUpload"
  />
</template>

<script setup>
import { ModelMediaManager } from '@mediables/vue';
</script>

Blade Components (Coming Soon)

# composer require mediables/blade-components
<x-mediables-upload
    :model="$product"
    collection="images"
/>

<x-mediables-gallery
    :model="$product"
    collection="images"
/>

Livewire Components (Coming Soon)

# composer require mediables/livewire-components
<livewire:mediables-manager
    :model="$product"
    collection="images"
/>

API Reference

The complete REST API is documented using OpenAPI 3.0.3:

Key API Groups

  • Media Management: Upload, list, update, delete media
  • Image Editing: Apply filters, crop, save variants
  • Video Processing: Encode, transcode, apply effects
  • Albums: Organize media into hierarchical albums (requires enable_albums=true)
  • Admin: Administrative endpoints for media management

Authentication

All API endpoints require authentication via Laravel Sanctum:

// Set authorization header
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;

Media Upload

POST /api/v1/media/upload
Content-Type: multipart/form-data

Parameters:
- file: File (required)
- collection: string (optional)
- custom_properties: object (optional)
- owner_uuid: string (required)
- owner_type: string (required)

Response:

{
  "data": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "file_name": "image.jpg",
    "mime_type": "image/jpeg",
    "size": 1024000,
    "collection_name": "gallery",
    "original_url": "https://cdn.example.com/media/image.jpg",
    "conversion_urls": {
      "thumb": "https://cdn.example.com/media/conversions/image-thumb.jpg",
      "preview": "https://cdn.example.com/media/conversions/image-preview.jpg"
    },
    "custom_properties": {},
    "created_at": "2025-01-24T10:00:00Z"
  }
}

List Media

GET /api/v1/media?collection=gallery&search=product&page=1&per_page=20

Apply Image Filters

POST /api/v1/media/manipulate
Content-Type: application/json

{
  "image_data": "data:image/jpeg;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
  "original_media_uuid": "550e8400-e29b-41d4-a716-446655440000",
  "crop_params": {
    "x": 0,
    "y": 0,
    "width": 100,
    "height": 100,
    "scaleX": 1,
    "scaleY": 1
  },
  "filter_params": [
    {"name": "ColorMatrix-1977"}
  ],
  "async": true
}

Response (Sync, 201):

{
  "data": {
    "uuid": "770e8400-e29b-41d4-a716-446655440000",
    "url": "https://cdn.example.com/media/manipulated.jpg"
  }
}

Response (Async, 202):

{
  "id": "job-uuid-123",
  "status": "queued",
  "status_url": "https://api.example.com/api/v1/media/manipulations/job-uuid-123"
}

Polling Status:

GET /api/v1/media/manipulations/{job_id}

Legacy Aliases (Deprecated):

  • media_uuid (alias for original_media_uuid)
  • manipulations (stored as metadata only)
  • Usage triggers X-Mediables-Deprecated header.

Generate Conversions

POST /api/v1/media/{media_uuid}/conversions

For complete API documentation, see docs/api.md.

Image Processing

Available Filters

Adjustment Filters

  • brightness (-100 to 100) - Adjust image brightness
  • contrast (-100 to 100) - Adjust contrast levels
  • saturation (0 to 300) - Control color saturation
  • hue (0 to 360) - Rotate hue values
  • grayscale (boolean) - Convert to black and white

Blur Effects

  • KawaseBlur - High-quality blur effect
  • ZoomBlur - Radial motion blur
  • MotionBlur - Directional motion blur
  • Bloom - Soft glow effect

Vintage & Artistic

  • ColorMatrix-1977 - Instagram-style vintage filter
  • Sepia - Classic sepia tone
  • OldFilm - Vintage film with grain
  • Dot - Halftone newspaper effect
  • CRT - Retro monitor effect

Distortion Effects

  • Twist - Spiral distortion
  • Shockwave - Ripple effect
  • BulgePinch - Lens distortion

Usage Examples

use Mediables\Services\Core\ImageProcessor;

$processor = app(ImageProcessor::class);

// Generate a conversion with custom config
$processor->generateConversion($media, 'custom', [
    'width' => 500,
    'height' => 500,
    'fit' => 'contain',
    'quality' => 80,
    'manipulations' => [
        'brightness' => ['level' => 20],
        'contrast' => ['level' => 25],
    ],
]);

Performance

Caching Strategy

The package implements multi-layer caching:

// URL caching (reduces database queries)
'enable_url_cache' => true,
'url_cache_duration' => 60, // minutes

// CDN integration
'cdn' => [
    'default' => env('MEDIA_CDN_URL'),
    'invalidate_on_change' => true,
],

// Lazy conversion generation
'lazy_conversions' => true,

Queue Configuration

Optimize performance with granular queue configuration:

'queues' => [
    'upload' => 'default',           // File uploads
    'conversions' => 'default',      // Conversion generation
    'conversions_high' => 'high',    // Priority conversions
    'conversions_low' => 'low',      // Background conversions
    'responsive' => 'default',       // Responsive image generation
    'delete' => 'low',              // File deletion
],

Database Optimization

  • Uses UUIDs for efficient lookups
  • Polymorphic relationships for flexibility
  • Indexed foreign keys and commonly queried columns
  • Optimized queries with eager loading

Monitoring

// Get performance metrics (debug mode only)
GET /api/v1/admin/media/performance

// Monitor conversion progress
GET /api/v1/media/{media}/conversions/progress

Artisan Commands

# Generate missing conversions
php artisan mediables:generate-conversions

# Clean up orphaned media records
php artisan mediables:cleanup-orphans

# Verify media files exist in storage
php artisan mediables:verify-files

# Warm CDN cache
php artisan mediables:warm-cache

# Test media relationships
php artisan mediables:test-relationships

# Test S3 connectivity
php artisan mediables:test-storage

Scheduling Orphan Cleanup

Scheduling is the consuming application's responsibility. This command is designed to be safe for cron/schedulers by using chunked processing and an optional runtime bound (--limit).

php artisan mediables:cleanup-orphans --grace-period=7 --chunk=200 --limit=2000

Testing

The package includes comprehensive testing support:

use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Mediables\Facades\MediaService;

// Fake storage for testing
Storage::fake('public');

// Create fake upload
$file = UploadedFile::fake()->image('product.jpg', 1200, 800);

// Test media upload
$media = MediaService::storeMediaFile($file, 'public', 'products');

// Assert file exists
Storage::disk('public')->assertExists($media->full_path);

// Test media attachment
$product = Product::factory()->create();
$product->attachMedia($media, 'gallery');

$this->assertTrue($product->hasMedia('gallery'));
$this->assertEquals(1, $product->getMedia('gallery')->count());

Migration from v1.x

Quick Migration

Run the migration command:

php artisan mediables:migrate-v2

This will:

  • Analyze your current setup
  • Update trait usage automatically
  • Migrate configuration
  • Provide detailed migration report

Manual Migration

  1. Update Traits:

| Old (v1.x) | New (v2.0) | |---|---| | Mediables\Traits\HasMediaCollections | Mediables\Traits\InteractsWithMediables |

  1. Update Method Calls:

| Old Method | New Method | |------------|------------| | getMediaByCollection() | getMedia() | | getFirstMediaByCollection() | getFirstMedia() | | hasMediaCollection() | hasMedia() | | detachMedia() | detachMedia() | | syncMediaCollection() | clearMediaCollection() + attachMedia() loop |

For detailed migration instructions, see UPGRADE.md.

Migration from Spatie Media Library

Migrating from Spatie Media Library? We've got you covered:

# The package includes backward compatibility
# Your existing database schema works with minor adjustments
# See detailed migration guide: docs/migration-guide.md

Key differences:

  • Uses UUIDs instead of auto-incrementing IDs
  • Improved polymorphic relationship structure
  • Enhanced querying capabilities
  • Better performance optimizations

For a complete migration guide, see docs/migration-guide.md.

Database Schema

Core Tables

  • media - Stores media file metadata and properties
  • mediables - Polymorphic pivot table linking media to models
  • media_variants - Tracks processing status of media variants
  • media_albums - Optional album organization (if enabled)
  • media_audits - Optional audit trail (if enabled)

Key Fields

Media Table:

uuid (primary key)
file_name
original_file_name
mime_type
size
disk
collection_name
custom_properties (JSON)
created_at, updated_at

Mediables Table:

media_uuid (foreign key)
mediable_uuid
mediable_type
collection_name
order_column

Requirements

  • PHP 8.1 or higher
  • Laravel 11.0 or higher
  • Intervention Image 3.11 or higher
  • Vue 3.4+ (for Vue components)
  • Ionic 8.0+ (for mobile components)

License

The MIT License (MIT). Please see License File for more information.

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for details.

Support & Documentation

Credits

  • Originally extracted from the Marketplace project
  • Built on top of Intervention Image
  • Inspired by Spatie Media Library
  • Vue components designed for Ionic Framework

Ready to get started? Check out our Quick Start Guide or dive into the full documentation.