Media Library - v1.0-beta1

Customization

This guide covers various ways to customize the Media Library package to fit your application's needs.

Publishing Assets

Publish Configuration

php artisan vendor:publish --tag=media-config

Configuration will be in config/artisanpack.php under the media key.

Publish Views

php artisan vendor:publish --tag=media-views

Views will be in resources/views/vendor/media/.

Publish Migrations

php artisan vendor:publish --tag=media-migrations

Migrations will be in database/migrations/.

Custom Image Sizes

Register custom image sizes in a service provider:

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Product thumbnails
        apRegisterImageSize('product-small', 200, 200, true);
        apRegisterImageSize('product-medium', 400, 400, true);
        apRegisterImageSize('product-large', 800, 800, false);
        
        // Hero banners
        apRegisterImageSize('hero-desktop', 1920, 600, true);
        apRegisterImageSize('hero-mobile', 768, 400, true);
        
        // Social media
        apRegisterImageSize('og-image', 1200, 630, true);
        apRegisterImageSize('twitter-card', 1200, 600, true);
    }
}

Use custom sizes:

$url = apGetMediaUrl($mediaId, 'product-medium');
$media->imageUrl('hero-desktop');

Custom Storage Disk

Configure Cloud Storage

Amazon S3:

In config/filesystems.php:

's3-media' => [
    'driver' => 's3',
    'key' => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION'),
    'bucket' => env('AWS_MEDIA_BUCKET'),
    'url' => env('AWS_URL'),
    'visibility' => 'public',
],

In .env:

MEDIA_DISK=s3-media
AWS_ACCESS_KEY_ID=your-key
AWS_SECRET_ACCESS_KEY=your-secret
AWS_DEFAULT_REGION=us-east-1
AWS_MEDIA_BUCKET=my-media-bucket

Google Cloud Storage:

'gcs-media' => [
    'driver' => 'gcs',
    'key_file_path' => env('GOOGLE_CLOUD_KEY_FILE'),
    'project_id' => env('GOOGLE_CLOUD_PROJECT_ID'),
    'bucket' => env('GOOGLE_CLOUD_MEDIA_BUCKET'),
    'visibility' => 'public',
],

Customizing Views

After publishing views, customize them:

{{-- resources/views/vendor/media/livewire/components/media-modal.blade.php --}}

<div>
    {{-- Add custom header --}}
    <div class="custom-header">
        <h2>{{ __('Select Media') }}</h2>
        <p class="text-muted">{{ __('Choose from your media library') }}</p>
    </div>

    {{-- Original content --}}
    @include('media::partials.media-grid')
</div>

Extending Models

Extend the base models to add custom functionality:

namespace App\Models;

use ArtisanPackUI\MediaLibrary\Models\Media as BaseMedia;

class Media extends BaseMedia
{
    /**
     * Get media usage count across the site.
     */
    public function usageCount(): int
    {
        $count = 0;
        
        // Count in posts
        $count += \App\Models\Post::where('featured_image_id', $this->id)->count();
        
        // Count in galleries
        $count += \DB::table('gallery_media')->where('media_id', $this->id)->count();
        
        return $count;
    }
    
    /**
     * Check if media is in use.
     */
    public function isInUse(): bool
    {
        return $this->usageCount() > 0;
    }
}

Then update the configuration:

// config/artisanpack.php
'media' => [
    'user_model' => App\Models\Media::class,
],

Custom Upload Processing

Create a custom upload service:

namespace App\Services;

use ArtisanPackUI\MediaLibrary\Services\MediaUploadService as BaseUploadService;

class CustomMediaUploadService extends BaseUploadService
{
    protected function afterUpload($media, $file): void
    {
        parent::afterUpload($media, $file);
        
        // Add custom watermark
        if ($media->isImage()) {
            $this->addWatermark($media);
        }
        
        // Generate AI descriptions
        $this->generateAIDescription($media);
    }
    
    protected function addWatermark($media): void
    {
        // Custom watermark logic
    }
    
    protected function generateAIDescription($media): void
    {
        // AI description generation
    }
}

Bind in service provider:

$this->app->bind(
    \ArtisanPackUI\MediaLibrary\Services\MediaUploadService::class,
    \App\Services\CustomMediaUploadService::class
);

Custom Validation Rules

Add custom validation in a Form Request:

namespace App\Http\Requests;

use ArtisanPackUI\MediaLibrary\Http\Requests\MediaStoreRequest as BaseRequest;

class CustomMediaStoreRequest extends BaseRequest
{
    public function rules(): array
    {
        return array_merge(parent::rules(), [
            'file' => [
                'required',
                'file',
                'max:' . config('artisanpack.media.max_file_size'),
                'mimes:jpg,png,gif,webp',
                function ($attribute, $value, $fail) {
                    // Custom validation: check image dimensions
                    if ($value->isValid()) {
                        [$width, $height] = getimagesize($value->path());
                        
                        if ($width < 800 || $height < 600) {
                            $fail('Image must be at least 800x600 pixels.');
                        }
                    }
                },
            ],
        ]);
    }
}

Hooks & Filters

Use hooks to modify behavior:

// Customize allowed MIME types
addFilter('media.allowed_mime_types', function ($types) {
    return array_merge($types, [
        'application/zip',
        'text/plain',
    ]);
});

// Modify upload path
addFilter('media.upload_path', function ($path, $file, $user) {
    // Organize by user ID
    return "media/users/{$user->id}/" . date('Y/m');
}, 10, 3);

// Add custom image sizes dynamically
addAction('media.register_image_sizes', function () {
    if (config('theme.enable_retina')) {
        apRegisterImageSize('thumbnail-2x', 300, 300, true);
        apRegisterImageSize('medium-2x', 600, 600, false);
    }
});

// Before media deletion
addAction('media.before_delete', function ($media) {
    // Check if media is in use
    if ($media->isInUse()) {
        throw new \Exception('Cannot delete media that is in use');
    }
});

// After media upload
addAction('media.after_upload', function ($media) {
    // Send notification
    Notification::send($admins, new MediaUploadedNotification($media));
});

Custom Livewire Components

Extend base components:

namespace App\Livewire;

use ArtisanPackUI\MediaLibrary\Livewire\Components\MediaModal as BaseMediaModal;

class CustomMediaModal extends BaseMediaModal
{
    public array $customFilters = [];
    
    public function mount(bool $multiSelect = false, int $maxSelections = 0, array $selectedMedia = [], string $context = ''): void
    {
        parent::mount($multiSelect, $maxSelections, $selectedMedia, $context);
        
        // Add custom initialization
        $this->customFilters = $this->getCustomFilters();
    }
    
    protected function getCustomFilters(): array
    {
        return [
            'recent' => __('Recently Uploaded'),
            'popular' => __('Most Used'),
            'unused' => __('Unused'),
        ];
    }
    
    public function applyCustomFilter($filter): void
    {
        // Apply custom filtering logic
    }
}

Use in views:

<livewire:custom-media-modal context="custom" />

Next Steps