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

thrace-project/media-bundle

Symfony bundle for managing media files and images: upload handling, storage and entity integration, reusable media entities, and helpers for attaching files to Doctrine-backed content. A practical foundation for media libraries and galleries.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package. Before anything else, confirm that your project is a Symfony full-stack app (or that you are willing to run one alongside Laravel). Laravel has no bundle/kernel/DI-extension system, so ThraceMediaBundle cannot be composer require'd into a Laravel app and "just work" — see Gotchas for what to use instead if you are Laravel-only.

For a Symfony app, the shortest path:

composer require thrace-project/media-bundle

Then read, in this order:

  1. composer.json — check the symfony/*, php, doctrine/* and oneup/uploader-bundle constraints. This package predates Symfony 4 flex and will almost certainly pin Symfony 2.x/3.x and PHP 5.6/7.x. If your app is Symfony 5+, stop here.
  2. DependencyInjection/Configuration.php — the actual config surface (media_class, upload directory, image filter definitions, entity mapping strategy).
  3. Entity/Media.php + MediaManager — the whole package is essentially "a Doctrine entity for a file row plus a service that writes bytes to disk and populates the row."
  4. Resources/views/ and Controller/ — the bundled media browser/upload endpoints you are expected to embed.

Register and configure:

// app/AppKernel.php
new Thrace\MediaBundle\ThraceMediaBundle(),
new Oneup\UploaderBundle\OneupUploaderBundle(), // upload transport this bundle leans on
# app/config/config.yml
thrace_media:
    media_class: AppBundle\Entity\Media   # your subclass of the bundle entity
    # upload_dir / file_system / image filters per Configuration.php

Map your entity (the bundle ships a mapped superclass or an entity you subclass — check which your version uses), then:

php app/console doctrine:schema:update --force

First use case to prove it out: attach one image to one entity, render it. Add a MediaType field to an existing form, upload a JPEG, confirm the row in media and the file on disk, and render the path in Twig. If that loop works end-to-end, the rest of the bundle is variations on it; if it doesn't, you've found the version-incompatibility problem early and cheaply.

Implementation Patterns

1. Form-driven single/multiple media attach. The primary integration is a form field, not a controller.

$builder->add('image', MediaType::class, [
    'type'     => 'image',   // file | image | video | audio
    'multiple' => false,
]);

Let the bundle's form type render its own widget (it wires up the JS uploader for you). Don't hand-roll a <input type="file"> next to it — you'll get two competing upload paths and one will silently produce orphan rows.

2. MediaManager as the only write path. Never persist a Media row yourself. Always go through the manager so file naming, path resolution and metadata (contentType, size, type) stay consistent:

$media = $this->get('thrace_media.media_manager')->createMedia($uploadedFile, 'image');

$entity->setImage($media);
$em->persist($entity);
$em->flush();

Centralizing this makes the eventual "we need S3 / a CDN / virus scanning" change a one-class change instead of a grep across controllers.

3. CMS-style media browser for existing assets. The bundle's real value over a plain file input is reuse: browse previously uploaded media and pick one. Embed the bundled controller via a route/modal in your admin, and have your form persist the chosen Media id rather than re-uploading. This is the pattern for a back office where editors upload once and reuse across pages.

4. Image manipulations at upload time, not render time. Configure resize/crop/rotate variants in bundle config and let the manager generate them on save. Generating thumbnails on every request is the classic way to make this bundle look slow; pre-generating also means your templates stay dumb (<img src="{{ media.path }}"> plus a variant path).

5. Wrap it. Put a thin MediaService of your own in front of the bundle's services:

class MediaService
{
    public function __construct(private MediaManager $manager) {}

    public function attach(UploadedFile $file, string $type): Media { /* ... */ }
    public function delete(Media $media): void { /* remove file + row */ }
}

You get one seam for testing, one place to add S3/orphan cleanup, and a fighting chance of swapping the package out later without touching controllers.

6. Lifecycle hooks for cleanup. Wire preRemove/postRemove on the owning entity to delete the underlying file. The bundle stores a path string; Doctrine will happily delete the row and leave the file behind forever.

Gotchas and Tips

Framework lock-in is the headline. This is a Symfony bundle. If you are a Laravel developer, the realistic assessment is: read it for ideas (media entity + manager + upload transport is the same shape as spatie/laravel-medialibrary), then use spatie/laravel-medialibrary for storage/associations and intervention/image for manipulations. Porting this bundle means re-implementing it. Do not budget for "adapting" it.

Maintenance signals are weak. 11 stars, no meaningful community, no security policy or recent activity. Treat the composer constraints as frozen: it targets an older Symfony/PHP generation, so PHP 8.1+ typed properties, Symfony 6 autowiring, and modern Doctrine will likely conflict. Pin exact versions and expect to vendor-patch. If you adopt it, do so with the assumption that you now maintain it.

Doctrine mapping surprises. Whether you subclass a mapped superclass or a concrete entity changes your migrations significantly. Decide before writing migrations; switching later means a schema rename dance. Also verify the media_class config points at your class, not the bundle's — a mismatch produces rows that don't hydrate.

Uploads land in a public directory with real filenames. There is no signed-URL or private-delivery story here. Validate MIME type and extension server-side (never trust the client), and be aware that "public path + guessable name" is a data-exposure pattern for anything user-submitted. If uploads are user-generated, put a reverse-proxy rule or a controller in front of the directory.

The upload endpoint is an HTTP surface you own. The file transport goes through a controller/route (historically OneupUploaderBundle + a JS uploader). Check that it is behind your firewall, that CSRF/session handling matches your app, and that it returns exactly the JSON shape the front-end JS expects. Most "upload does nothing" bugs are a 403 from the firewall or a response-format mismatch, not a bundle bug.

Orphaned files and orphaned rows. Two failure modes, both common: upload succeeds but the form never persists the entity (file on disk, no row); and entity deleted but file remains. Add a console command or cron that reconciles disk vs. DB — you will need it.

Debugging checklist, in order: (1) is the upload directory writable by the web user; (2) does thrace_media config actually load (dump the container, look for the media_manager service); (3) does the route exist (debug:router | grep media); (4) does the JS get a 200 JSON response; (5) did the row persist with a non-null path. That sequence resolves the large majority of integration failures.

Extension points worth knowing. Templates and translations can be overridden the Symfony way (app/Resources/ThraceMediaBundle/...), which is how you avoid forking just to change markup. The MediaManager is the intended service-level extension point for storage backends and manipulation logic — subclass or decorate it rather than editing vendor code.

Verdict for day-to-day use: viable only inside a legacy Symfony 2/3 application that already depends on it. For new work — Laravel or modern Symfony — the patterns are worth copying and the package itself is not worth adopting.

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.
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment