johnkrovitch/media-bundle
A PHP media bundle from johnkrovitch for managing and serving media files in web applications. It aims to simplify uploads, storage, and asset handling, but with minimal adoption and sparse documentation it is best suited to experimentation rather than production use.
Before anything else: this is not a Laravel package. johnkrovitch/media-bundle is a Symfony bundle ("Media Bundle for Cms"), published as a Composer package but wired for Symfony's kernel, DI container, and Doctrine. In a Laravel app you can composer require it, but nothing will register itself — no service provider, no config publish, no facade. Treat it as reference source code, not a dependency.
If you are actually on Symfony, the minimal path is:
composer require johnkrovitch/media-bundle and check composer.json in the package for its symfony/* and php constraints against your app — a 2020-vintage bundle will likely cap out at Symfony 4/5 and PHP 7.x.config/bundles.php (find the namespace root in the package's src/ — the class name is the bundle's *Bundle.php file).src/DependencyInjection/ for the extension alias (this is the config key you'll use under config/packages/*.yaml) and Configuration.php for the exact tree of options. Do not guess option names — this package has no meaningful docs, so Configuration.php is the real documentation.src/Resources/config/ for services.yaml, routing, and Doctrine mappings. Note whether mappings are XML/YAML/annotations — if annotations, they'll break on PHP 8 / newer Doctrine.bin/console doctrine:schema:update --dump-sql first to see what tables it wants before committing a migration.First use case for a CMS: a shared media library — upload an asset, persist metadata, generate derivatives (thumbnails), and reference the media entity from content entities via a relation rather than storing raw paths.
If you are on Laravel — which is the likely situation — do this instead:
| Bundle capability | Laravel equivalent |
|---|---|
| Media entity + Doctrine mapping | Eloquent model with morphTo |
| Filesystem abstraction | Storage facade + config/filesystems.php disks |
| Image derivatives | intervention/image or spatie/image |
| Upload handling / validation | FormRequest + image/mimes rules |
| Twig rendering helpers | Blade component + <x-media> |
| Admin list/edit | Filament or Nova field |
spatie/laravel-medialibrary already covers ~90% of what this bundle does, is actively maintained, and has thousands of dependents. Read this bundle's src/ for domain modeling ideas, then implement against Spatie. Budget half a day for that read; it is likely small.
Symfony-side wiring (if you proceed there). The bundle almost certainly follows the classic CMS pattern: a Media-ish entity with fields for filename, original name, mime type, size, and a folder/category relation, plus a service responsible for persisting the file and writing derivatives. Expect the entity to be extended by your app entity (Doctrine inheritance or a simple ManyToOne from your content entity), because CMS bundles of this era assume you'll own the association. Override the bundle's Twig templates by placing files of the same name under templates/bundles/<BundleName>/ — this is the standard Symfony override path and the safest extension point.
The workflow to replicate in Laravel:
upload request → validate (mime, size, extension) → store original to
a non-public disk with a generated name → create Media row (uuid, name,
mime, size, disk, path, owner morph) → dispatch ProcessMedia job →
worker generates derivatives → update Media with derivative paths →
serve via signed/route URL, never a raw public path
Keep originals immutable and derivatives regenerable. Never store a derivative path without also storing the transform parameters (width, height, fit) — otherwise you can't rebuild the library when you change image processing.
Eloquent model sketch:
class Media extends Model
{
protected $guarded = ['id'];
protected $casts = ['meta' => 'array', 'size' => 'integer'];
public function mediable(): MorphTo { return $this->morphTo(); }
public function url(string $conversion = 'original'): string
{
return Storage::disk($this->disk)->url($this->pathFor($conversion));
}
}
Add a HasMedia trait with morphMany(Media::class, 'mediable') and a registerMediaConversions() hook — that shape mirrors what the bundle gives you in Symfony and keeps the port honest.
Integration tips. Make the storage disk a config value, not a constructor argument, so tests can swap to Storage::fake(). Do derivative generation in a queued job so an image upload doesn't hold the request for 3 seconds. If the bundle exposes a post-persist event (look for Event/ or *Event.php in src/), that's the right seam for your own thumbnailing/virus-scan/EXIF-stripping; the Laravel port is a model observer or an event listener on MediaCreated.
ContainerBuilder signatures) has likely moved. Do not put this on a critical path.finfo rather than trusting the client-supplied type, reject double extensions (shell.php.jpg), generate filenames server-side, store outside the webroot, and ensure the upload directory can't execute PHP (php_flag engine off / nginx location ~ \.php$ { deny all; } inside uploads).Storage::put() and $media->save() leaves an orphan file; a delete that removes the row but fails on the file leaves an orphan blob. Add a scheduled reconciliation command, and prefer deleting files via a queued job after the row is gone so failures are retryable.Model::deleting hooks or a prunable model to clean up, and test it.config/packages/; resolve the alias from DependencyInjection/*Extension::getAlias(), not from the README. Upload-directory values are usually %kernel.project_dir%-relative parameters — a common failure is a path that resolves correctly in dev but not in a container where the project dir is read-only.bin/console debug:container | grep -i media to confirm services registered, and the profiler's Doctrine panel to see the queries. On a Laravel port: Storage::fake() in tests catches path bugs early, and php artisan about won't help — check config/filesystems.php and the storage:link symlink first when URLs 404.How can I help you explore Laravel packages today?