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

ranky/media-bundle

A PHP media bundle for managing uploaded files and image assets in Symfony applications. It helps organize media, integrate reusable file-handling features, and provide a clean foundation for adding storage and media management to your project.

View on GitHub
Deep Wiki
Context7

Getting Started

Framework reality check first. ranky/media-bundle is a Symfony bundle (Ranky\MediaBundle), not a Laravel package. There is no service provider, no config file in config/, no facade. If your stack is Laravel-only you have three realistic paths: (a) stand up a small Symfony "media service" alongside Laravel and talk to it over HTTP, (b) run the Symfony bundle in a hybrid repo, or (c) skip it and use spatie/laravel-medialibrary. The rest of this assessment assumes you've chosen (a) or (b) and need to actually operate the bundle.

Minimal install:

composer require ranky/media-bundle
  1. Register the bundle in config/bundles.php:
    Ranky\MediaBundle\RankyMediaBundle::class => ['all' => true],
    
  2. Import routing in config/routes.yaml (prefix it so you can lock it down later):
    ranky_media:
        resource: '@RankyMediaBundle/config/routes.php'
        prefix: /admin/media
    
    Verify the actual route file name against the bundle's Resources/config/ directory — it's a small package and this is the first thing that drifts between releases.
  3. Configure storage. The bundle leans on Flysystem. You need a league/flysystem-bundle filesystem configured (local disk for dev, S3 adapter for prod) and the bundle's ranky_media config pointing at it. A working local disk is the fastest path to a first upload.
  4. Run the migrations the bundle ships:
    bin/console doctrine:migrations:migrate
    
  5. Install the admin assets (React UI is pre-built):
    bin/console assets:install public
    

Where to look first. In order: the bundle's config/routes.php (that's your entire API surface), Entity/MediaFile.php (the thing you'll be associating to), the URL-generator service (check Resources/config/services.php for the exact service id), and the compiled React admin under public/. Read those four and you understand 90% of the package.

First use case: a drop-in media library for a CMS — upload images through the React admin, associate a MediaFile with your Post entity, and render the public URL in a template. That's the happy path the bundle is optimized for; everything else is stretching it.

Implementation Patterns

Entity association. Treat MediaFile as a first-class Doctrine entity and own the relationship from your side: a ManyToOne for single-image fields (Post::$cover), ManyToMany for galleries. Persist only the id; resolve the URL at read time through the generator rather than storing a full URL string. Storing URLs is the classic mistake — it breaks the moment you move from local disk to S3 or introduce a CDN prefix.

URL generation. Inject the bundle's URL generator service into your controllers/twig extensions and centralize calls in one place (a MediaPresenter or Twig extension). When you later need signed S3 URLs, you decorate that one service instead of grepping for URL concatenation across the codebase.

Flysystem as the abstraction layer. This is the bundle's main integration win: swap the underlying filesystem, keep the same API. Configure your S3 filesystem in flysystem.yaml, point ranky_media at it, and the bundle neither knows nor cares. Do the same for a CDN by configuring the URL prefix in the bundle config.

REST API for decoupled frontends. The main day-to-day pattern for a Laravel dev: Laravel (or a SPA) never touches Doctrine. It uploads via multipart/form-data to the bundle's endpoint, gets back a media id, stores that id in its own database, and calls a small Symfony endpoint (or a Laravel controller that shells out to the Symfony service) to resolve id → URL. This keeps the media runtime isolated and lets you swap it later.

// Laravel side — thin client, no coupling to Doctrine
$response = Http::withToken(config('services.media.token'))
    ->attach('file', $bytes, 'hero.jpg')
    ->post('https://media.internal/api/files');
$mediaId = $response->json('id');

WYSIWYG / editor integration. Point CKEditor's or TinyMCE's file-browser URL at the bundle's folder-listing and upload endpoints. Same for a file picker in the admin — the React UI already speaks this API, so reuse the endpoints it calls (watch the network tab once and you have the contract).

Programmatic / batch uploads. Don't loop HTTP calls for bulk imports. Use the bundle's file-manager service directly from a Symfony console command: it handles storage writes, entity creation, and any image-variant generation in one transaction-ish flow. Much faster and you get consistent metadata.

Multi-tenant via storage prefixes. The bundle ships no tenancy. The clean workaround is a Flysystem path prefix per tenant (one filesystem, per-tenant root) rather than separate filesystems — keeps IAM policies simple and lets the bundle's folder logic just work inside the tenant's subtree.

Gotchas and Tips

No authentication is bundled. The REST API and the admin route are open by default. If you import routing with a public prefix and forget security.yaml, you've shipped an unauthenticated file uploader. Minimum:

access_control:
    - { path: ^/admin/media, roles: ROLE_ADMIN }

and put rate limiting on the upload endpoint.

Doctrine schema conflicts. The shipped migrations assume specific table names (likely media_file, media_folder, translations, etc.). Audit them before running — if you have an existing media table or use UUID/ULID primary keys, you'll get collisions or FK type mismatches. For a greenfield Symfony app, fine; for an existing schema, expect to hand-edit the migration.

Flysystem version coupling. Check the bundle's composer constraints against your league/flysystem-bundle and league/flysystem-aws-s3-v3 versions. Flysystem v3 had breaking changes across majors, and small bundles often pin tightly. A version conflict here will surface as a "filesystem service not found" error at container compile time, not a nice message.

Image processing is config-sensitive. Thumbnails typically ride on liip/imagine-bundle. The failure mode is silent: thumbnail URLs return 404 because the imagine cache resolver points at a different filesystem than the one you configured for ranky_media. Verify both point at the same disk, and confirm GD or Imagick is actually installed (php -m | grep -i imagick).

Changing storage after go-live. Moving local → S3 or adding a CDN: update the URL base in the bundle config and migrate existing files. The bundle won't do the copy for you. Orphaned local files are the second most common post-migration bug (the first is stale cached URLs).

Deletion semantics. Check whether MediaFile is soft-deletable. If it is, remove() on the entity leaves the blob in Flysystem forever. Always delete through the bundle's manager service so entity and storage stay in sync. Periodic reconciliation for orphans is worth writing — 20 lines of console command.

React admin blank page. The pre-built assets need assets:install and a public path that your web server serves. If you're on AssetMapper or a custom Encore setup, verify the bundle's public/ directory is exposed at the expected URL. A blank /admin/media with a 404 on a JS chunk is almost always an assets-install problem, not a bundle bug.

Debugging checklist. bin/console debug:router | grep media to see exactly what's exposed, bin/console debug:container | grep ranky for service ids, and always check the network tab against the API before assuming the admin UI is broken. The bundle is small enough that vendor/ranky/media-bundle/src is readable in an afternoon — do that when stuck rather than googling; 68 stars means the Stack Overflow surface is approximately zero.

Extension points. Decorate rather than fork: wrap the URL generator for CDN signing, wrap the file manager for virus scanning / MIME validation / audit logging. If the bundle supports class-mapping overrides in its config (many Symfony bundles do), swap in your own MediaFile entity subclass to add fields — check the config tree before committing to an external join table.

Community risk. 68 stars, single-maintainer cadence, last release 2025-11-27. Pin the version in composer.json, vendor the migration files into your project if they're sensitive to future changes, and accept that you may be reading source rather than docs. For a Laravel team this is a real operational cost — weigh it against a native Laravel package before committing to the sidecar architecture.

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.
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource