Weave Code
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

drewroberts/media

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require drewroberts/media
    php artisan vendor:publish --provider="DrewRoberts\Media\MediaServiceProvider"
    

    This publishes the migration files and config. Run migrations:

    php artisan migrate
    
  2. Configure Cloudinary: Add credentials to .env:

    CLOUDINARY_CLOUD_NAME=your_cloud_name
    CLOUDINARY_API_KEY=your_api_key
    CLOUDINARY_API_SECRET=your_api_secret
    
  3. First Use Case: Upload an image via a model (e.g., Post):

    use DrewRoberts\Media\Models\Image;
    
    $post = new Post();
    $post->title = "My Post";
    $post->save();
    
    $image = $post->images()->create([
        'path' => 'path/to/local/file.jpg',
        'alt' => 'Post thumbnail',
        'title' => 'Thumbnail',
    ]);
    

    The package automatically uploads to Cloudinary and stores metadata in the DB.


Implementation Patterns

Core Workflows

1. Model Relationships

Attach media to Eloquent models via polymorphic relationships:

// In Post model
public function images()
{
    return $this->morphMany(Image::class, 'model');
}

public function videos()
{
    return $this->morphMany(Video::class, 'model');
}

2. Bulk Uploads

Use MediaManager for batch operations:

use DrewRoberts\Media\Facades\MediaManager;

$images = MediaManager::uploadMultiple(
    request()->file('images'),
    'posts/123',
    ['alt' => 'Gallery images']
);

3. Tagging Media

Assign tags to media for categorization:

$image->tags()->attach([1, 2, 3]); // Attach existing tags
$image->tags()->create(['name' => 'New Tag']); // Create new tag

4. YouTube Video Handling

Store YouTube videos with metadata:

$video = $post->videos()->create([
    'url' => 'https://youtu.be/abc123',
    'title' => 'Embedded Video',
    'description' => 'Video description',
]);

5. Transformations

Generate Cloudinary transformations on-the-fly:

$image->transform('fill', 300, 200)->getUrl();
// Outputs: https://res.cloudinary.com/.../fill_300_200/image.jpg

6. Local Fallback

Use local storage as a fallback (configure in config/media.php):

'fallback' => [
    'driver' => 'local',
    'path' => storage_path('app/public/media'),
],

Integration Tips

Form Requests

Validate and handle file uploads in a FormRequest:

public function rules()
{
    return [
        'images.*' => 'required|image|mimes:jpeg,png,jpg|max:2048',
    ];
}

public function withValidator($validator)
{
    $validator->after(function ($validator) {
        if ($validator->errors()->has('images')) {
            return;
        }
        // Process uploads
    });
}

API Responses

Return media URLs with transformations:

return response()->json([
    'image_url' => $image->transform('crop', 500, 500)->getUrl(),
    'thumbnail_url' => $image->transform('thumbnail', 100, 100)->getUrl(),
]);

Admin Panels

Use with Laravel Nova or Filament for media management:

// Nova Resource
public static $mediaFields = [
    MediaField::make('Images', 'images'),
    MediaField::make('Videos', 'videos'),
];

Caching

Cache transformed URLs to reduce Cloudinary API calls:

$url = Cache::remember("media_{$image->id}_transformed", now()->addHours(1), function () use ($image) {
    return $image->transform('fill', 300, 200)->getUrl();
});

Gotchas and Tips

Pitfalls

  1. Cloudinary Credentials:

    • Gotcha: Forgetting to set CLOUDINARY_API_SECRET will cause silent failures.
    • Fix: Verify credentials with:
      php artisan media:test-connection
      
  2. Polymorphic Relationships:

    • Gotcha: Forgetting to define morphMap in AppServiceProvider can cause issues:
      public function boot()
      {
          \DrewRoberts\Media\Models\Image::morphMap([
              'post' => \App\Models\Post::class,
              // Add other models
          ]);
      }
      
  3. File Size Limits:

    • Gotcha: Cloudinary has file size limits. Large files may fail silently.
    • Fix: Validate file sizes client-side and log errors:
      if ($file->getSize() > 50 * 1024 * 1024) { // 50MB
          Log::error("File too large: {$file->getClientOriginalName()}");
      }
      
  4. YouTube URL Parsing:

    • Gotcha: Malformed YouTube URLs (e.g., missing youtu.be) may break video storage.
    • Fix: Sanitize URLs before saving:
      $url = preg_replace('/^(https?:\/\/)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)\//i', 'https://youtu.be/', $url);
      
  5. Local Fallback:

    • Gotcha: Local fallback requires storage:link to be run:
      php artisan storage:link
      

Debugging

  1. Log Uploads: Enable debug mode in config/media.php:

    'debug' => env('MEDIA_DEBUG', false),
    

    Check logs for upload errors:

    tail -f storage/logs/laravel.log | grep media
    
  2. Cloudinary API Errors:

    • Use the Cloudinary Console to inspect failed uploads.
    • Check the failed_jobs table for queued jobs that failed.
  3. Database Issues:

    • Run php artisan media:prune to clean up orphaned media records.

Extension Points

  1. Custom Transformations: Extend the DrewRoberts\Media\Transformations\Transformation class to add custom Cloudinary transformations:

    namespace App\Media\Transformations;
    
    use DrewRoberts\Media\Transformations\Transformation;
    
    class CustomTransformation extends Transformation
    {
        public function __construct($width, $height, $effect)
        {
            $this->width = $width;
            $this->height = $height;
            $this->effect = $effect;
        }
    
        public function getTransformation()
        {
            return "e_{$this->effect}:w_{$this->width}_h_{$this->height}";
        }
    }
    
  2. Custom Storage Drivers: Implement DrewRoberts\Media\Contracts\StorageDriver for non-Cloudinary storage (e.g., AWS S3):

    namespace App\Media\Drivers;
    
    use DrewRoberts\Media\Contracts\StorageDriver;
    
    class S3Driver implements StorageDriver
    {
        public function upload($file, $path, $options)
        {
            // Custom S3 upload logic
        }
    
        public function getUrl($path, $options = [])
        {
            // Custom URL generation
        }
    }
    
  3. Events: Listen to media events for custom logic:

    // In EventServiceProvider
    protected $listen = [
        'DrewRoberts\Media\Events\MediaUploaded' => [
           \App\Listeners\LogMediaUpload::class,
       ],
    

];


4. **Middleware**:
Restrict media access with middleware:
```php
namespace App\Http\Middleware;

use Closure;
use DrewRoberts\Media\Models\Image;

class VerifyMediaOwnership
{
    public function handle($request, Closure $next)
    {
        $image = Image::findOrFail($request->route('image'));
        if ($image->model->user_id !== auth()->id()) {
            abort(403);
        }
        return $next($request);
    }
}

Performance

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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle