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

jolicode/media-bundle

JoliCode MediaBundle is a Symfony bundle for handling images, videos, and audio. It offers a provider system, on-the-fly filters like resize and crop, responsive image helpers, and local or cloud storage integration, making media consistent and easy to reuse across your application.

View on GitHub
Deep Wiki
Context7

Getting Started

First, the honest framing: this is a Symfony bundle, not a Laravel package. There is no service provider, no facade, no Blade directive, no Eloquent integration, and no config/media.php. Its value is concentrated in things that only exist inside a Symfony kernel: a Doctrine-mapped Media entity, DI-configured "libraries" over Flysystem, on-demand image variation generation, a browser controller/route, and EasyAdmin/SonataAdmin/Sylius field integrations. In a pure Laravel codebase you will not "leverage this package day-to-day" — you'll either avoid it or read it as a reference implementation.

If you do have a Symfony app in the estate (common in mixed shops):

  1. composer require jolicode/media-bundle — requires PHP 8.2+, Symfony 6.4/7.x, Doctrine ORM, league/flysystem-bundle, and GD or Imagick. As of v0.10.0, imagine/imagine is a default (required) dependency — image manipulation now goes through Imagine's ImagineInterface rather than a bundle-internal convert step, so installs get it whether or not they configured it before.
  2. Accept the Flex recipe (registers the bundle + config/packages/jolicode_media.yaml).
  3. Define one library: an original storage pointing at a Flysystem filesystem, and a cache storage for generated variations.
  4. Run bin/console doctrine:migrations:diff && bin/console doctrine:migrations:migrate for the media table.
  5. bin/console assets:install / rebuild your front-end — v0.10.0 replaced Dropzone with an internal uploader, so the shipped JS/CSS for the media widget is now the bundle's own; don't expect your old Dropzone overrides to still apply.
  6. Create an Article entity with a ManyToOne to the bundle's Media, expose it in EasyAdmin with the bundle's media field, upload an image, render a named variation in Twig.

Where to look first: the README's configuration reference (libraries → storages → variations), then src/Library/, src/Storage/, src/Form/, src/Twig/, and — new in 0.10 — the srcset Resolver that now owns what used to live in the Twig extension, plus whatever backs temporary urls. Start with the Storage and Library classes — that's the whole architecture in two files — then read the Resolver before touching any responsive-image templates.

First use case worth shipping: "editor uploads a hero image through the admin, front end renders a responsive srcset from pre-declared variations." That's the 20-minute win, and it's the only reason to adopt the bundle over hand-rolling.

Implementation Patterns

  • One library per context. Define default, avatars, blog libraries each with their own Flysystem storage — conceptually identical to Laravel disks. This is the cleanest way to separate public CDN assets from private uploads, and it maps 1:1 to how you'd configure filesystems.disks in Laravel.
  • Media is an entity, reference it by relation. You don't inline a path string on Article; you add a ManyToOne to the media table. That's what enables reuse ("pick an existing image" instead of re-uploading) and metadata queries (alt text, dimensions) without touching the filesystem.
  • Declare variations in config, reference them by name. thumb, card, hero etc. defined once; templates never compute resize parameters. Variations are generated lazily on first access and then cached — the pattern is the bundle's CachedStorage sitting in front of the original storage. In 0.10 the srcset widths are assembled by a dedicated Resolver rather than in Twig, so the responsive logic is testable and reusable outside templates (useful if you expose media over JSON/API). Mirror this in Laravel with a controller route that checks Storage::disk('media-cache'), generates via intervention/image, streams, and returns a long-cache response — and put the width-set calculation in a small service, not a Blade component with inline loops.
  • Use temporary urls for private libraries. v0.10.0 adds expiring/temporary URL support — this is the correct answer for the private uploads library rather than hand-rolling a signed route. In Laravel terms it's Storage::disk('s3')->temporaryUrl(...); the point is the bundle now gives you that seam, so don't invent one.
  • Use the processability check for mixed media. 0.10 lets you tell from a path alone whether a media can be processed. Gate thumbnails/variation rendering on that instead of on mime-type guessing or a filename ends with .jpg hack — it's the bundle's own notion of "is this an image we can convert."
  • Upload path: the bundle's media form type handles the uploaded file, mime detection, and dimension extraction. Persist the entity in the same transaction as the owning entity; don't write your own mover. If you had JS hooked into the old Dropzone markup, budget time to re-check the widget after upgrading.
  • Prewarm on deploy. Run the bundle's variation-generation console command in your release pipeline rather than letting the first anonymous visitor eat the cost (see Gotchas).
  • Non-image media is still second-class, but now detectable. Video/PDF metadata is captured and variations remain image-only — use the original path for those, and gate on the processability check rather than building a UI that assumes every media has a thumbnail.
  • EXIF orientation is handled (fixed in 0.10) — you no longer need a custom post-process step to rotate phone photos, and existing cached variations of sideways images are worth clearing once after the upgrade.
  • Laravel equivalent, if you're porting the pattern: spatie/laravel-medialibrary gives you the DB entity + filesystem + registerMediaConversions(); add a signed on-demand conversion route to replicate the lazy-cache behavior, intervention/image v3 for the actual processing, and a small SrcsetResolver service mirroring the bundle's Resolver. That combination is roughly the Laravel-shaped version of this bundle minus the admin UI.

Gotchas and Tips

  • Don't try to bridge it into Laravel. You'd be writing a container bridge for a Doctrine entity + Symfony form types + admin field classes — more work than reimplementing the useful 20% with Spatie. Treat this package as a design reference, not a dependency.
  • v0.10.0 is a minor bump on a pre-1.0 package — treat it as potentially breaking. The two changes most likely to bite: (a) srcset generation moved from Twig to a Resolver (if you overrode the Twig extension, replaced templates, or called the old function directly, re-check your output and markup); (b) imagine/imagine is now required, and the convert step moved into core — any custom convert/adapter wiring or service overrides you wrote around the old arrangement needs re-verification. Also re-check anything that assumed Dropzone was present.
  • Upgrade for the trash_path normalization alone. 0.10 fixes a path-handling bug where the trash directory could be deleted. If you rely on soft-deleted media surviving, don't sit on 0.9.
  • The admin integrations are the actual product. EasyAdmin/SonataAdmin/Sylius fields and the media browser are what justify the dependency — and 0.10 spent effort there (generic fa icon class, Sylius widget/upload-link fixes, upload markers). If you're not on any of those admin stacks, what's left is "a media entity plus Flysystem sugar" — and that's a weekend of work.
  • Lazy generation hides latency. First hit per variation per media is slow, and a page with 30 images × 3 srcset widths triggers a lot of work. Prewarm in CI/deploy and watch memory for large originals. The Resolver move makes it easier to see exactly which widths a template will request — use that list as your prewarm input.
  • Stale variations after config changes. Changing a variation's dimensions does not invalidate the cache. You must clear the cache storage (or version the variation name) — otherwise you'll debug "why is the thumbnail 200px" for an hour. Same applies to EXIF/orientation fixes: old cached files stay wrong until purged.
  • Multi-node + S3. If the cache storage is local and the original is S3, variation generation happens on whichever web node served the request. Either put the cache on shared object storage or front it with the bundle's controller route so all nodes converge. 0.10 added S3 integration tests, so the S3 path is better exercised than it was — but tests don't fix your local-cache topology. Same trap exists in Laravel with storage:link on ephemeral containers.
  • SVG and "images" generally. The bundle validates uploads, but a public disk plus SVG is an XSS vector. Enforce your own allowlist on the form/adapter — don't assume the media type covers it, and don't assume the processability check implies "safe to serve inline."
  • Temporary URLs need a storage that supports them. Signed/expiring links work against capable adapters (S3-style); a plain local-filesystem storage will not honour expiry the way you expect. Match the library's storage to the URL mechanism, and don't treat a temporary URL as revocation — the underlying object is still public if the disk is.
  • Deletion semantics. Confirm (in the source for your pinned version) whether removing the entity also removes the original and cached files. If not, you'll accumulate orphans; add a scheduled cleanup. 0.10 tightened trash-path handling, so re-verify your cleanup job still points where you think it does.
  • Debugging: bin/console debug:config jolicode_media to see resolved libraries, debug:container for the storage services and the image/Imagine services, and the bundle's own log channel for variation generation failures. In Laravel terms: php artisan config:show and a dedicated log channel.
  • Extension points are clean and worth copying: decorate StorageInterface (logging, virus scan, metrics), swap the path generator if you want hash-based paths instead of slug-based, override form theme blocks for the admin widget, and now swap/extend the srcset Resolver if your breakpoints differ from the default. These are the same seams you'd want in a Laravel media package — which is the strongest argument for reading this source even if you never install it.
  • Bus factor. ~119 stars, 0 dependents, single-vendor maintenance, but a healthy 0.10 release cadence and — notably — three first-time contributors plus S3 integration tests, which is a good sign for a package this small. Still: fine for an internal admin tool inside a Symfony app; risky as a foundational piece of a multi-year product. Read the source, and pin the version.
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.
robertmarney/lara-hierarchial-collections
simonschaufi/rule-doc-generator
emissaoja/emissaoja
blackbricksoftware/gravityforms-civicrm-form-processor
al-rahabi/fancy-crud
jinas/dhivatar-component
mltstephane/laravel-quality
bernskiold/laravel-talentlms
bernskioldmedia/laravel-form-architect
youbar/easy-crud
c-point-labs/laravel-hostyourai
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo