enhavo/media-bundle
Enhavo MediaBundle is a subtree split of the open source Enhavo CMS, a flexible PHP project built on the Symfony fullstack framework for managing complex data structures through a clean, usable interface. See the main repository for details.
This package is a read-only subtree split of the main enhavo/enhavo repository. The README is not a usage guide — it only points back to the monorepo. Treat the repository itself as the documentation: open composer.json, src/, DependencyInjection/, Resources/config/, and Entity/ first.
Minimal activation:
composer require enhavo/media-bundle
// config/bundles.php
return [
// ...
Enhavo\Bundle\MediaBundle\EnhavoMediaBundle::class => ['all' => true],
];
Then inspect the bundle's service definitions and routing:
php bin/console debug:container | grep -i media
php bin/console debug:router | grep -i media
Look first for:
Entity/ or Model/ — the media/file abstraction (File, Image, Video, Audio or similar).Form/Type/ — the upload form type you will embed in your own admin forms.Controller/ — upload/download endpoints and their route names.Resources/config/ — services, Doctrine mappings, and routing.Admin/ — Enhavo's admin resource configuration if you are using the CMS admin.Twig/ — URL/thumbnail helpers for rendering media in templates.A sensible first use case: attach a media entity to one of your own Doctrine entities, upload it through an admin form, and render its public URL and a thumbnail in Twig. That exercises the full path — storage, metadata, entity association, and rendering — before you customize formats or storage.
Entity association. The common pattern is a ManyToOne (or OneToOne) from your domain entity to the bundle's media entity. Persist the association, not the raw file path, so the bundle remains responsible for storage and URL resolution.
#[ORM\ManyToOne(targetEntity: Media::class)]
private ?MediaInterface $image = null;
Confirm the exact interface/class name from src/ before writing mappings — the bundle distinguishes media types, so you may need FileInterface rather than a concrete class.
Form integration. Embed the bundle's media form type in your own Symfony form and let it handle upload and existing-media selection:
$builder->add('image', MediaType::class, [
'required' => false,
]);
In the admin context, Enhavo's resource/admin configuration is usually the better integration point than hand-rolling forms. Check Admin/ for how the bundle registers media as an admin resource, then mirror that pattern for your own resources.
Programmatic uploads. For imports or API endpoints, inject the bundle's media manager service, create a media object from a UploadedFile or local path, persist it through the manager, and flush. Prefer the manager over manually moving files — it centralizes filename generation, metadata, and storage abstraction.
Rendering. Use the Twig extension rather than concatenating upload paths. Typical usage looks like:
<img src="{{ media_url(entity.image) }}" alt="">
<img src="{{ media_url(entity.image, 'thumbnail') }}" alt="">
Verify filter/format names against Resources/config/ and any LiipImagine (or equivalent) configuration. The same applies to download links and video/audio rendering: use the bundle helper so storage changes do not break templates.
Formats and media types. The bundle is designed around media formats (image sizes/variants) and type-specific behavior. Add project-specific formats in bundle configuration or by extending the relevant services, rather than hardcoding dimensions in templates. If you need a new media type, look for the type registry/resolver and register a new service tagged the same way as the built-in image/video/audio types.
Admin workflow. Media is typically managed as its own admin resource (upload, list, edit metadata), then selected from other resources via the media form type. Use that two-step workflow instead of embedding raw uploads everywhere; it gives you a reusable media library and consistent permissions.
doctrine:schema:update is acceptable in production; generate and review migrations.debug:config for the relevant bundle and check the generated cache directory.enhavo_media namespace by convention. When overriding templates or services, use the full bundle path (e.g. templates/bundles/EnhavoMediaBundle/...) and verify overrides with debug:twig or debug:container --show-private.debug:router, inspect the upload directory permissions, and confirm the media format/filter exists. When uploads fail silently, check form validation constraints and the manager service's exception handling.How can I help you explore Laravel packages today?