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

rmzamora/media-bundle

A PHP media management bundle for handling uploads, storage, and image processing. It provides configurable storage adapters, thumbnail generation, metadata handling, and a simple API for attaching media files to your application's models and entities.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the composer.json before you read anything else. This is a fork of sonata-project/media-bundle sitting at 0 stars, 0 dependents, archived. That means: no security patches, no Symfony/PHP version bumps, and no upstream sync. Treat it as a vendored snapshot of a specific commit rather than a package you "install." If you're in a Laravel app, you almost certainly do not want this — it's a Symfony bundle with hard dependencies on sonata-project/admin-bundle, sonata-project/core-bundle, sonata-project/block-bundle, Doctrine ORM/ODM, and Imagine. The realistic day-to-day use is: you inherited a Symfony 2.x/3.x app that already has it, or you're evaluating it to decide you'd rather use spatie/laravel-medialibrary.

Assuming a Symfony app that can run it:

composer config repositories.media-bundle vcs https://github.com/rmzamora/SonataMediaBundle
composer require rmzamora/media-bundle:"dev-master"

Then in app/AppKernel.php (or config/bundles.php):

new Sonata\CoreBundle\SonataCoreBundle(),
new Sonata\BlockBundle\SonataBlockBundle(),
new Sonata\AdminBundle\SonataAdminBundle(),
new Sonata\MediaBundle\SonataMediaBundle(),

Minimal config.yml:

sonata_media:
    db_driver: doctrine_orm
    default_context: default
    contexts:
        default:
            providers: [sonata.media.provider.image, sonata.media.provider.file]
            formats:
                small: { width: 100, quality: 80 }
                big:   { width: 500, quality: 80 }
    cdn:
        server:
            path: /uploads/media
    filesystem:
        local:
            directory: "%kernel.root_dir%/../web/uploads/media"

Then bin/console doctrine:schema:update --force (or generate a migration) and add the download route:

sonata_media_download:
    resource: "@SonataMediaBundle/Resources/config/routing/media.xml"

Where to look first: DependencyInjection/Configuration.php (the source of truth for every config key), Document/Media.php and Entity/Media.php (the model is duplicated for ODM/ORM — you'll subclass these), Provider/ImageProvider.php and Provider/AbstractProvider.php (thumbnail generation lives here), and Admin/MediaAdmin.php (the UI you'll be extending). Ignore the README.

First use case: attach a Media to an existing entity, then render a thumbnail.

// AppBundle/Entity/Post.php
/** @ORM\ManyToOne(targetEntity="Application\Sonata\MediaBundle\Entity\Media", cascade={"persist"}) */
private $cover;

// in your controller / form handler
$media = new Media();
$media->setBinaryContent($uploadedFile);   // Symfony UploadedFile
$media->setContext('default');
$media->setProviderName('sonata.media.provider.image');
$mediaManager->save($media, 'default', 'sonata.media.provider.image');

$post->setCover($media);   // cascade persists the join
{# small = the format name from config; respects your CDN path automatically #}
<img src="{% path post.cover, 'small' %}" alt="">

The path and thumbnail Twig functions are the only supported way to build URLs — hand-concatenating web/uploads/media/... will silently break the day someone flips the CDN or moves to S3.

Implementation Patterns

Design around contexts, not around entities. A context (news, avatar, gallery) bundles a provider list and a set of formats. Every format you declare is a file written to disk at persist time, so small/big/reference is already 3 files per upload. Adding medium, square, og "just in case" is the #1 way people blow up storage and upload latency. Start with one format, add on demand, and accept that changing formats later means a regeneration pass.

Always go through sonata.media.manager.media. The manager (MediaManagerInterface) owns persistence, provider dispatch, and thumbnail generation. Bypassing it with $em->persist($media) gives you a DB row with no files and a broken path. The three calls you'll write constantly:

$mediaManager->save($media, $context, $providerName); // create/update + generate thumbs
$mediaManager->get($id);                              // fetch by id
$manager->getProvider($name)->getFormatName($media, $format);

Galleries: use Sonata's collection type, not a hand-rolled many-to-many. sonata_type_collection with by_reference => false on a MediaCollection-like entity is the pattern the bundle's admin templates expect; rolling your own join table works but you lose the drag-to-reorder and inline upload UX that justifies using Sonata Admin at all.

Swap the filesystem via config, never via code. sonata.media.filesystem.local and sonata.media.cdn.server are the two you'll touch. For S3, register an adapter implementing FilesystemInterface and point sonata_media.filesystem at it — but note the CDN path must be publicly reachable and already populated by your upload path. The bundle writes via the filesystem abstraction and links via the CDN config; it does not upload to your CDN for you.

Defer heavy processing out of the request. Thumbnail generation happens synchronously inside save() via Imagine. For user-uploaded photos this is where your 30s timeouts come from. The pragmatic pattern: persist the original with a single reference format, then queue a job that re-runs generation with the display formats on. On the Laravel side this maps to dispatch(new GenerateConversions($media)) around spatie/laravel-medialibrary, which does exactly this by default.

Laravel mental model, for translation:

Sonata Media Laravel equivalent
context + formats collection + conversions
MediaManager::save() $model->addMedia()->toMediaCollection()
sonata.media.filesystem Storage disk / Flysystem
path/thumbnail Twig fns $media->getUrl('thumb')
MediaAdmin Filament / Nova file field
provider (image, youtube) custom FileAdder / path generator

If you're building greenfield in Laravel, stop reading here and use the right column.

Gotchas and Tips

Namespace collision is the first wall. The fork keeps the Sonata\MediaBundle namespace. Installing it alongside any sonata-project/* package that depends on sonata-project/media-bundle produces a conflict or, worse, an autoload race where you get a random version of Provider/ImageProvider.php. Check whether the fork declares replace in its composer.json; if it doesn't, you need a path repository plus explicit replace entries.

Archived means the dependency graph is frozen. Expect symfony/symfony constraints around ~2.3|~3.0, PHP >=5.3, and Doctrine ~2.2. On PHP 8.2 / Symfony 6 this will not boot. Before committing any time, run composer why-not php 8.2 and composer update --dry-run and see how bad it is.

getBinaryContent() is transient. It's the staging slot for the upload, cleared after save(). Re-reading it later returns null and that's why your "re-generate thumbnails" script does nothing. Re-generation must go through the provider's file path, or the bundle's console commands — bin/console list sonata:media to see what this fork actually ships (the command names moved around between versions, and this fork may predate the rename).

Soft-delete leaves files on disk. MediaAdmin's delete sets a flag; the file remains until a purge command runs. If you're auditing storage growth, query the DB for flagged rows and list the filesystem directory — they will not agree.

Format changes require a backfill. Editing a format width in config only affects newly generated thumbnails. Existing rows keep the old file until regenerated. Budget for this every time you touch contexts.*.formats.

Debugging checklist for "no thumbnail / 404":

  1. bin/console debug:container sonata.media.provider.image — is the provider registered with the config you think?
  2. var/logs/dev.log filtered on sonata.media — provider exceptions are often swallowed and rendered as an empty <img>.
  3. Confirm the file exists under web/uploads/media/<context>/... — if it's there but 404s, your CDN path is wrong.
  4. Confirm the media's context matches a key in sonata_media.contexts — a mismatch throws at render time, not save time.

Memory and image size. GD is the default adapter (sonata.media.adapter.image.gd); Imagick is faster and handles CMYK/large JPEGs better. Set post_max_size/upload_max_filesize realistically and cap dimensions before generation — a 40MP phone photo will exhaust a 128M memory_limit inside Imagine and the failure mode is a blank page, not an exception.

Extension points worth knowing: Provider\ProviderInterface (implement for S3-direct or video services), Resizer\ResizerInterface (simple, square — the square crop is a common "why is my thumbnail stretched" fix), Thumbnail\ThumbnailInterface for per-format options, and Generator\GeneratorInterface for URL strategies. All are container services — override via a compiler pass or a service definition with the same id, and never edit the bundle in vendor/.

Vendor it if you must keep it. Fork, pin a commit, and add it to your own repo with a replace block so the rest of the world can't pull a different copy. The upstream sonata-project/media-bundle is maintained, has the same API for the parts described above, and the migration is mostly namespace/version bumps — that's a far better long-term bet than this archive.

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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource