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

libre-informatique/media-bundle

A Symfony bundle for managing media files and images, providing Doctrine-backed media entities, file upload and storage handling, and reusable tools for attaching pictures and documents to application content.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate expectations: this is a Symfony bundle, and you are a Laravel developer. There is no service provider, no facade, no config/media.php, no Eloquent model. composer require libre-informatique/media-bundle into a Laravel app will install code that nothing autoloads or bootstraps. The realistic day-to-day relationship with this package is "read it, don't depend on it" — borrow the domain model, reimplement it idiomatically.

Where to look first, in this order:

  1. composer.json — the fastest way to learn what this thing actually assumes. Look at require for the symfony/* constraints, doctrine/orm, and any filesystem/image dependency (Flysystem, Gaufrette, LiipImagineBundle, Imagine). Also check autoload PSR-4 prefixes so you can map namespaces to directories.
  2. Entity/ — the Media model and its Doctrine mapping. This is the part worth keeping: which fields exist (path, filename, mime, size, dimensions, timestamps), whether attachments are polymorphic, and how storage location is recorded.
  3. DependencyInjection/ and Resources/config/ — the service definitions. This tells you the real API surface: a media manager service, a storage/upload adapter, probably a form type or two and a Twig helper.
  4. README + last commit date — treat the README as documentation of intent. With 0 stars, 0 dependents and a NOASSERTION license, nothing here is battle-tested.

First use case to evaluate against: the boring universal one — upload a file, persist a record pointing at it, and later serve or derive from it (thumbnail, crop, resize). Everything else in the bundle is scaffolding around that flow.

Minimal spike (in a throwaway Symfony skeleton, never in your Laravel repo):

composer create-project symfony/skeleton media-spike
cd media-spike
composer require libre-informatique/media-bundle
# register the bundle, point the storage adapter at var/media,
# run the schema diff, upload one file through the provided form type

Then verify the invariant that matters: the row and the file agree. If the spike doesn't work end-to-end in an afternoon, stop — the package hasn't earned further time. Cap the whole investigation at a few hours and write down what you learned about the entity shape.

Implementation Patterns

Pattern A — Consuming it (Symfony host app only)

You only get to use this pattern if you're already in a Symfony/Sylius codebase, ideally the Libre Informatique orbit.

  • Configure storage via env, not defaults. The bundle's defaults almost certainly assume a pre-Symfony 4 web/uploads layout. Override with %env(MEDIA_STORAGE_PATH)% and a filesystem adapter you control.
  • Single write path. Go through the bundle's manager service for writes; never persist() the media entity directly from a controller. The manager sequences file write + DB write, and bypassing it is how you get orphaned rows and orphaned files.
  • Keep the association on your side. Relate your domain entities to Media with a Doctrine association you own (ManyToOne, or a join table for a shared library). That way you can excise the bundle later without a migration archaeology project.
  • Push derivatives off the request. Whatever image pipeline the bundle wires (likely LiipImagine), move thumbnail generation to a queue message. Upload requests should write bytes and a row, nothing more.
  • Extend by decoration, not editing. If you vendor the code, wrap the manager/storage service in a decorator, and hook lifecycle work into Symfony event subscribers rather than patching the bundle classes. Vendored edits are the thing you'll regret at the first security patch.

Pattern B — Porting the model to Laravel (the realistic path)

Mirror the triad, not the code:

// database/migrations/..._create_media_table.php
$table->id();
$table->morphs('mediable');        // the thing the Symfony bundle can't give you
$table->string('disk');
$table->string('path');
$table->string('collection')->default('default');
$table->string('mime');
$table->unsignedBigInteger('size');
$table->string('original_name');
$table->json('conversions')->nullable();
$table->timestamps();

A small MediaManager service with one entry point:

public function store(Model $owner, UploadedFile $file, string $collection = 'default'): Media
{
    return DB::transaction(function () use ($owner, $file, $collection) {
        $path = $file->store("media/{$collection}", $owner->mediaDisk());

        $media = $owner->media()->create([
            'disk'          => $owner->mediaDisk(),
            'path'          => $path,
            'collection'    => $collection,
            'mime'          => $file->getClientMimeType(),
            'size'          => $file->getSize(),
            'original_name' => $file->getClientOriginalName(),
        ]);

        GenerateConversions::dispatch($media)->afterCommit();

        return $media;
    });
}

Key patterns that carry over:

  • Polymorphic mediable morph relation instead of Doctrine associations. This is the single biggest ergonomic win and the reason to port rather than adopt.
  • Declarative collections/derivatives — a config array like ['thumb' => ['w' => 200, 'h' => 200], 'hero' => ['w' => 1600]] — so conversions are data, not bespoke code per model.
  • Manager as the only write path, same discipline as the Symfony side. Controllers call store(), replace(), delete(); nothing touches Storage directly outside the manager.
  • Queued conversions with afterCommit() so the row exists before a worker looks for it.

Gotchas and Tips

  • NOASSERTION license is a hard gate, not a nitpick. Legal cannot clear it from a metadata scan. That means: no redistribution, no embedding in shipped artifacts, and if you port the code line-for-line you may be copying unlicensed source. Read it for design, then write your own — don't cut and paste, and don't vendor the files into a proprietary repo without review.
  • Zero dependents means you are the QA department. No one has stress-tested the API, no upgrade guides exist, no security advisories route to you. Adoption flips the cost model from "integration" to "fork ownership."
  • Framework coupling is total. Doctrine entities, the DI container, Symfony forms and Twig helpers. There is no clean seam to lift the storage layer into Laravel; you'd be reimplementing it anyway.
  • Version pins will bite. Expect a narrow symfony/* constraint and transitive Doctrine/Imagine pins. Run composer why-not (or a resolver dry run in a throwaway skeleton) before assuming compatibility with anything modern.
  • File + DB are not transactional together. Filesystem writes don't roll back. Order writes as "file first, then row," and add a reconciliation command (scan storage for unreferenced paths, scan rows for missing files) — this is the bug you will hit, and it's worth 30 lines of code regardless of which library you use.
  • Default paths assume legacy Symfony directory layout. If anything writes to web/uploads, fix it at config level before your first upload, not after.
  • Extension points to look for: the manager service (decorate it), the storage adapter interface (swap local→S3 behind it), form types (reuse or replace), and Twig helpers (replace with a Blade component or an accessor on the model). If the bundle exposes no storage interface, treat that as a red flag about S3 readiness.
  • Don't let it into composer.json. The correct dependency for a Laravel media subsystem is spatie/laravel-medialibrary (MIT, maintained, polymorphic, S3-native, queued conversions). Use this bundle's entity shape as a design checklist against it, confirm the feature gap is nil, and move on.
  • Debugging tip: when a ported upload "works" but the file 404s, check disk resolution before anything else — the disk column and Storage::disk() config drift apart the moment you add a second disk, and it presents as a path bug.
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.
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
uni-method/symfony-lib