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

opifer/media-bundle

Opifer MediaBundle is a Symfony bundle for managing and displaying media assets such as images, videos, and files. It provides media entities, upload handling, and Twig helpers for embedding media in templates.

View on GitHub
Deep Wiki
Context7

Getting Started

Latest tag is 0.1.3 — it adds editing of existing media items and fixes to run cleanly on Symfony 2.8. That confirms the supported target: PHP 5.5–7.0 and Symfony ~2.7/2.8. Treat the release as the ceiling, not a sign of life: the bundle is archived and there are no PHP 8 / Symfony 4+ fixes coming. If you're coming from Laravel, note up front that this is a Symfony bundle — no service provider, no config/media.php, AppKernel-style container wiring. The install path below is Symfony; the portable ideas (provider pool, storage abstraction) are what you'd re-implement in Laravel with spatie/laravel-medialibrary + Intervention Image + Flysystem.

Minimal path:

  1. composer require opifer/media-bundle (if Packagist still resolves it — otherwise vendor it from the GitHub tag).
  2. Register in AppKernel: new Opifer\MediaBundle\OpiferMediaBundle().
  3. Import its routing (the bundle ships a controller for the upload/picker endpoints) in app/config/routing.yml.
  4. Publish config: php app/console config:dump-reference opifer_media.
  5. Create your own Media entity extending the bundle's mapped superclass and implementing its interface. The bundle does not ship a concrete entity — this is the first real task, and it is the step people miss. Map it (@ORM\Entity), then doctrine:schema:update or generate a migration.
  6. Point the bundle config at your entity class and at your storage/filesystem settings.

Look first at DependencyInjection/Configuration.php for the real config surface, and the Model/ + Provider/ directories for the extension contracts. Docs are thin; the code is the spec.

First use case: a Product with a ManyToOne to your Media entity, a file picker in the admin form, and an image thumbnail rendered in Twig. That exercises every layer — storage, provider, manager, templating helper — and tells you fast whether the bundle is worth keeping.

Implementation Patterns

Mapped-superclass inheritance. You own the entity, the table, and the migrations; the bundle owns the behaviour. Keep your entity thin — a subclass with the table name and any extra relations — and let the bundle's manager do persistence and file handling. This is the cleanest way to add your own columns (e.g. alt, caption, sortOrder) without patching vendor code.

Manager/repository injection. Resolve the manager from the container (opifer_media.manager or the class-based service ID — check debug:container opifer_media) rather than constructing entities by hand. Uploads should go through the manager so storage naming, provider detection, and metadata stay consistent.

Provider pool. The bundle routes uploaded media through per-type providers (image, generic file, and remote types like YouTube/Vimeo). When rendering, use the provider/type to decide the template — images get Imagine filters applied, remote types render as embeds, generic files become download links. If you need a new type (e.g. SVG or a DAM-sourced asset), implementing the provider interface and tagging the service is the sanctioned extension point.

Storage abstraction. Swapping local filesystem for Gaufrette/Flysystem is a config change, not a code change, as long as you always go through the manager. Do this early — it's much cheaper than retrofitting after paths are baked into the database.

Editing media (new in 0.1.3). The picker/manager now exposes an edit flow for existing media, so metadata edits (name, alt/caption, provider selection) no longer need a hand-built admin form. Route edits through the manager too, so a changed file or provider re-runs the same storage/naming path as upload; if you map extra columns, add them to your own form type and merge them in. Treat the edit flow as the bundle's UI, not your model's write path.

Form integration. Embed the bundle's media picker form type into your own admin form types (Sonata Admin or plain Symfony forms), and link the edit action into your list/detail views. For rich text, wire the picker to CKEditor/Redactor through the bundle's controller endpoint — the modal returns an ID you store alongside the body HTML, and you resolve it at render time.

Rendering. Thumbnails come from Imagine filters; always render a filter variant rather than the original for list views, and cache-bust on the stored filename rather than the media ID.

Lifecycle. Add a Doctrine listener on your entity's preRemove/postRemove to delete the underlying file. The bundle predates widespread orphanRemoval usage; without a listener you will accumulate dead files indefinitely. Re-saving through the 0.1.3 edit flow doesn't clean up superseded files either.

Gotchas and Tips

  • Archived means frozen — 0.1.3 is the high-water mark. No PHP 8 support, no ContainerBuilder autowiring, and the bundle-registration style won't survive a Symfony 4+ upgrade. The 2.8 compatibility fixes make 2.8 the sweet spot, not a launchpad. Treat any adoption as a fork-and-maintain decision.
  • The missing entity is the #1 stumbling block. Nothing works until your subclass is mapped; the manager will fail with a class-not-found or "not a valid entity" error until the mapping cache is cleared.
  • Clear the cache after every config or entity change — annotation/XML mapping caches hide the real error and produce misleading "class does not exist" messages.
  • Editing is record-level, not file-level. 0.1.3 lets you edit media items; verify whether replacing the underlying file actually regenerates Imagine thumbnails, and whether the old file is deleted — assume neither until you've tested it, and handle both in your listener/save path.
  • Files are not garbage-collected. Deleting a Media row leaves the file on disk and any Imagine thumbnail cache untouched. Write the listener, and put thumbnail cache in a path you can prune with a cron.
  • Storage paths ≠ web paths. web/ (pre-Symfony 4) is the docroot; the storage adapter writes elsewhere. Check the generated URL against what actually exists on disk when images 404 — it's almost always a path/naming mismatch, not a permissions issue.
  • Permissions bite on upload, not on render. Verify the webserver user can write to the upload directory before blaming the bundle.
  • Allowed extensions and max size are enforced inconsistently between the picker controller and the manager. Validate on your own form type too if uploads are user-facing.
  • Debugging: app/console debug:container opifer_media to find the real service IDs, debug:router for the picker endpoints, and the dev log for provider-pool resolution failures.
  • Extension points are the code contracts, not config: custom provider (tagged service), custom storage adapter, and template overrides under app/Resources/OpiferMediaBundle/views/. All three are stable enough to lean on.
  • Modern alternative: if you're greenfield — especially on Laravel — the equivalent stack is VichUploaderBundle + LiipImagineBundle + Flysystem (or SonataMediaBundle for Symfony admin; spatie/laravel-medialibrary + Intervention Image on Laravel). Borrow this bundle's provider-pool idea — it's the genuinely good part — but not its code.
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.
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
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core