Weave 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 Bundle Laravel Package

cesurapp/media-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the Package:

    composer require cesurapp/media-bundle cesurapp/storage-bundle
    

    Ensure your config/bundles.php includes both bundles.

  2. Configure Storage: Edit config/packages/cesurapp_storage.yaml to define your storage backends (e.g., local filesystem, S3). Example:

    cesurapp_storage:
        storages:
            default:
                type: local
                path: '%kernel.project_dir%/public/uploads'
            s3:
                type: aws_s3
                bucket: 'my-media-bucket'
                region: 'us-east-1'
    
  3. Add Media to an Entity: Extend your entity with MediaTrait and implement MediaInterface. For a single media column (e.g., avatar), this is optional:

    use Cesurapp\MediaBundle\Entity\MediaInterface;
    use Cesurapp\MediaBundle\Entity\Traits\MediaTrait;
    
    class User implements MediaInterface
    {
        use MediaTrait;
    
        // Optional: Define media columns for multi-column support
        public function getMediaColumns(): array
        {
            return ['avatar', 'document'];
        }
    }
    
  4. Upload Media via Controller: Use the MediaManager to handle uploads with built-in image processing:

    use Cesurapp\MediaBundle\Manager\MediaManager;
    use Symfony\Component\HttpFoundation\Request;
    
    class UploadController
    {
        public function upload(Request $request, MediaManager $manager): void
        {
            $medias = $manager
                ->setImageCompress(true)
                ->setImageQuality(80)
                ->uploadFile($request, ['avatar']);
    
            $user->addMedias($medias);
            $em->persist($user);
            $em->flush();
        }
    }
    
  5. Serve Media: Access media via entity methods or directly from storage:

    // In a controller or service
    $response = $user->getAvatarFirst()->getResponse('default');
    return $response;
    

First Use Case: User Avatar Upload

  • Goal: Allow users to upload and update profile avatars with automatic compression.
  • Steps:
    1. Add avatar column to User entity using MediaTrait.
    2. Create a form with file upload field (enctype="multipart/form-data").
    3. Use MediaManager in the controller to process the upload, compress the image, and attach it to the user.
    4. Display the avatar using getAvatarFirst()->getResponse().

Implementation Patterns

Core Workflows

1. Multi-Media Column Management

For entities requiring multiple media columns (e.g., avatar, document, cover_photo), extend MediaTrait and override getMediaColumns():

class Product implements MediaInterface
{
    use MediaTrait;

    public function getMediaColumns(): array
    {
        return ['image', 'thumbnail', 'certificate'];
    }
}
  • Tip: Use separate traits for each media type (e.g., AvatarTrait, DocumentTrait) to avoid bloating the entity and improve readability.

2. Image Processing Pipeline

Configure image transformations in the MediaManager chain:

$medias = $manager
    ->setImageCompress(true)
    ->setImageConvertJPG(true)       // Convert PNG/GIF to JPG
    ->setImageQuality(75)
    ->setImageSize(1200, null)       // Preserve aspect ratio
    ->uploadFile($request, ['image']);
  • Common Use Cases:
    • Thumbnails: Chain multiple MediaManager calls with different sizes.
    • Format Conversion: Use setImageConvertWebP(true) for modern formats.
    • Lazy Processing: Defer transformations until media is first accessed (configure via cesurapp_storage.yaml).

3. Reference Counting and Cleanup

Leverage automatic cleanup when media is no longer referenced:

// Remove media from entity (counter decrements)
$user->removeMedia($media);
$em->flush(); // Media deleted if counter reaches 0

// Entity deletion cascades to all media
$em->remove($user);
$em->flush(); // All media auto-deleted
  • Advanced: Override MediaTrait to customize cleanup logic (e.g., log deletions).

4. Base64 and Remote URL Uploads

Handle non-file uploads (e.g., Base64 from frontend or direct URLs):

// Base64 upload with MIME validation
$medias = $manager->uploadBase64(
    $request,
    ['image'],
    ['image' => ['image/png', 'image/jpeg']]
);

// Remote URL download
$medias = $manager->uploadLink(
    $request,
    ['remoteImage'],
    ['remoteImage' => ['image/webp']]
);

5. Storage-Agnostic Access

Retrieve media regardless of storage backend:

// Get response for HTTP serving
$response = $media->getResponse('s3'); // Uses 's3' storage config

// Get file path (storage-agnostic)
$path = $media->getPath(); // e.g., "2025/01/20/abc123.jpg"

// Get raw content
$content = $media->getContent('default');

Integration Tips

1. Form Handling

Use Symfony’s File type in forms:

use Symfony\Component\Form\Extension\Core\Type\FileType;

$builder->add('avatar', FileType::class, [
    'mapped' => false, // MediaBundle handles mapping
    'label' => 'Profile Picture',
]);
  • Tip: Use mapped => false to let MediaManager handle file processing.

2. Event-Driven Extensions

Extend functionality via Symfony events. Example: Log media uploads:

// config/services.yaml
services:
    App\EventListener\MediaUploadListener:
        tags:
            - { name: kernel.event_listener, event: media.pre_upload, method: onPreUpload }

class MediaUploadListener
{
    public function onPreUpload(MediaEvent $event): void
    {
        $media = $event->getMedia();
        // Custom logic (e.g., validation, logging)
    }
}

3. Custom Storage Adapters

Extend cesurapp/storage-bundle to support additional backends (e.g., Backblaze B2):

// src/Storage/BackblazeStorage.php
use Cesurapp\StorageBundle\Storage\StorageInterface;

class BackblazeStorage implements StorageInterface
{
    // Implement required methods (e.g., upload, delete, getUrl)
}

Register the adapter in cesurapp_storage.yaml:

cesurapp_storage:
    storages:
        backblaze:
            type: backblaze
            bucket: 'my-bucket'
            key: '%env(BACKBLAZE_KEY)%'

4. Doctrine Lifecycle Callbacks

Use Doctrine events for pre/post media operations:

// src/EntityListener/MediaLifecycleListener.php
use Doctrine\ORM\Events;

class MediaLifecycleListener
{
    public function prePersist(Media $media): void
    {
        // Example: Auto-generate filename
        $media->setName('auto_' . uniqid() . '.' . $media->getMimeType());
    }
}

Register in config/doctrine.yaml:

doctrine:
    orm:
        entity_listeners:
            Cesurapp\MediaBundle\Entity\Media: [App\EntityListener\MediaLifecycleListener]

5. Command-Line Management

Use built-in commands for maintenance:

# Check storage stats
bin/console media:status

# Cleanup orphaned media (run periodically)
bin/console media:cleanup

Gotchas and Tips

Pitfalls

1. Entity Trait Collisions

  • Issue: Using MediaTrait in multiple entities with the same column names (e.g., avatar) can cause conflicts.
  • Fix: Rename traits per entity (e.g., UserAvatarTrait, ProductImageTrait) or use unique column names.

2. Reference Counting Delays

  • Issue: Media files may linger in storage if the counter isn’t decremented immediately (e.g., due to transactions or lazy loading).
  • Fix: Ensure removeMedia() is called in the same transaction as the entity update. For complex workflows, use Doctrine events:
    $em->getEventManager()->addEventListener(
        Events::postRemove,
        function (LifecycleEventArgs $args) {
            $entity = $args->getObject();
            if ($entity instanceof MediaInterface) {
                $entity->cleanup(); // Force cleanup
            }
        }
    );
    

3. Image Processing Overhead

  • Issue: Heavy image processing (e.g., resizing large files) can block HTTP requests.
  • Fix: Use Symfony’s Messenger component for async processing:
    $message = new Process
    
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.
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo
stevenay/myanfont
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin