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

eo/media-bundle

A Symfony bundle for managing media files and attachments in your application. It provides upload handling, media entities, storage abstraction, and helpers for serving and organizing images and documents, making it easier to add reusable media management to Symfony projects.

View on GitHub
Deep Wiki
Context7

Getting Started

Framework reality check first. eo/media-bundle is a Symfony 2 bundle, not a Laravel package. There is no service provider, no facade, no config/media.php. You cannot composer require eo/media-bundle inside a Laravel app and get working code — the bundle's runtime is the Symfony DI container, AppKernel, and Doctrine's Symfony bridge. If you are a Laravel developer, treat this repository as a reference implementation of a media/upload domain model, not as a dependency. Everything below describes (a) how it is used in its native Symfony habitat and (b) what to lift from it into a Laravel equivalent.

Where to look first (in this order):

  1. README.md — will be terse; do not expect a full guide.
  2. Model/Media.php (or Entity/Media.php) — the mapped superclass / base entity. This is the real spec: which columns exist (name, path, mimeType, size, timestamps), which lifecycle callbacks are registered, and how file rename/delete is hooked.
  3. Resources/config/services.yml (or *.xml) — shows the MediaManager, the Twig extension, and the form type, plus what configuration keys are injected.
  4. DependencyInjection/Configuration.php — the authoritative list of config keys (upload_dir, web_dir, base_url, image dimensions, etc.).
  5. Form/Type/MediaType.php — how the file input is wired to the entity.

Minimal native setup (Symfony 2.x era):

// composer.json
"eo/media-bundle": "dev-master"

// AppKernel.php
new Eo\MediaBundle\EoMediaBundle(),

// app/config/config.yml
eo_media:
    upload_dir: %kernel.root_dir%/../web/uploads/media
    web_dir:    %kernel.root_dir%/../web
    base_url:   /uploads/media

Then create your own entity extending the bundle's model (or map the provided entity directly), set up the MediaType form field with enctype="multipart/form-data", and persist through the MediaManager.

First use case to prove it works: a single image upload. Render the form, POST a file, assert the file lands in upload_dir with a generated name, the Media row is inserted, and {{ media.webPath }} (or the Twig helper) renders a working URL. If that path works end-to-end, everything else in the bundle is variations on it.

For a Laravel port, the first milestone is equivalent: a Media Eloquent model + HasMedia trait + a Storage::disk('media')->putFile() call + an accessor for the public URL. Do that before considering anything else.

Implementation Patterns

1. Entity-extension pattern (Doctrine mapped superclass). The bundle expects you to own the concrete entity. You create AppBundle\Entity\Media extends Eo\MediaBundle\Model\Media with your own @ORM\Table / @ORM\Entity, and the parent supplies fields and callbacks. This is the single most important pattern to understand because it is also the pattern that makes the bundle hard to drop into an existing schema — the parent class dictates column names you may already be using.

Laravel analogue: a Media model plus a HasMedia trait on the owning models (one-to-many or morph-many), rather than inheritance. Laravel has no mapped-superclass equivalent worth using here.

2. Lifecycle-callback file handling. The bundle performs filesystem work in Doctrine callbacks — typically prePersist/preUpdate to move the uploaded UploadedFile into the configured directory and stamp the generated name, and postRemove/preRemove to unlink. The entity therefore owns I/O.

Pitfall to pre-empt in a port: doing storage I/O in model events means transactions and the filesystem drift apart. In Laravel, prefer an explicit MediaManager/action class called from the controller (or a queued job), and keep the model dumb.

3. Manager-as-service pattern. Eo\MediaBundle exposes a MediaManager service that holds the config (upload dir, web dir, base URL) and does the file moves and path resolution. Controllers and forms depend on the manager, not on the entity, for I/O. This is the cleanest part of the design and worth copying verbatim in spirit: a MediaManager bound to a Laravel Storage disk.

4. Named image transformations. Configuration typically declares named sizes/filters, and the Twig extension or manager produces derived files (thumbnails, crops). Day-to-day usage is: declare sizes once in config, reference them by name in templates.

Laravel analogue: intervention/image with a config/media.php of named conversions, generated on upload or on first request, cached on the media disk. Keep the named-conversion idea; drop the bundle's implementation.

5. Form-type integration. MediaType wraps a FileType plus the entity, handling the "upload replaces existing file" case. The pattern is that the form binds the raw UploadedFile, and the manager/callback handles persistence — the form never touches the filesystem.

Laravel analogue: a StoreMediaRequest FormRequest validating image|max:..., and a controller that hands the file to the manager.

6. Path vs. web path separation. Two accessors, one returning the absolute filesystem path and one returning the publicly reachable URL built from base_url + filename. Always use the web-path accessor in views and the filesystem path in code that manipulates files. Mixing them is the most common source of "image exists but 404s" bugs.

7. In a Laravel app: recommended integration shape.

// app/Actions/StoreMedia.php
final class StoreMedia
{
    public function __construct(private string $disk = 'media') {}

    public function handle(UploadedFile $file, $owner = null): Media
    {
        $path = $file->store('', $this->disk); // disk root = uploads/media

        return Media::create([
            'disk'       => $this->disk,
            'path'       => $path,
            'name'       => $file->getClientOriginalName(),
            'mime_type'  => $file->getClientMimeType(),
            'size'       => $file->getSize(),
            'mediable_id'   => $owner?->id,
            'mediable_type' => $owner ? $owner::class : null,
        ]);
    }
}

Then a Media::url() accessor using Storage::disk($this->disk)->url($this->path), and a Twig/Blade helper for named conversions. This gives you 90% of the bundle's value with none of the Symfony coupling.

Gotchas and Tips

Abandonment and platform. The repository targets Symfony 2.x and PHP 5.x. Expect @ORM annotations (not attributes), AppKernel registration, and possibly symfony/symfony constraints that conflict with anything modern. Composer will fight you on PHP 8. Treat it as archive material; do not plan to run it in production on a current stack.

Documentation is thin. The README will not describe the config keys completely. DependencyInjection/Configuration.php is the source of truth for options, and the services definition tells you which ones are actually consumed. Read both before trusting a blog post.

Config path mismatches. upload_dir must be an absolute filesystem path and must be inside web_dir for the generated base_url to resolve. A trailing slash on base_url or a relative upload_dir produces broken URLs that look like permission errors. Verify by dumping the resolved path and checking file_exists() in a tinker-style console.

Filename collisions and sanitisation. The bundle generates names, but check the strategy: if it preserves user-supplied names you get collisions and path-traversal risk. In any port, always generate the stored name yourself (Str::uuid() + original extension) and never trust the client filename as a path segment.

Orphaned files. Deletion is callback-based, so DELETE via raw SQL, bulk deletes, or failed transactions leave files behind (and vice-versa). In Laravel, schedule a media:prune command that reconciles rows against Storage::allFiles() — this is the operational habit that makes any media layer survivable.

Permission errors masquerading as logic errors. When an upload silently fails, check is_writable() on the upload directory and the temp directory before debugging the entity. Symfony 2-era code often swallows the exception in a callback.

Form encoding. Forgetting enctype="multipart/form-data" yields a null UploadedFile with no error on the entity — the file simply never arrives. This is still the #1 media bug in every framework.

Extension points (native). You can override the bundle's services by redefining the service id in your app config, and you can replace the manager or Twig extension with your own implementation as long as you honour the interface. This is the intended escape hatch; use it rather than forking the bundle.

Caching and URL generation. webPath values are usually generated at render time from config, so changing base_url later invalidates every stored URL. Persist the relative path only and resolve the absolute URL at render — never store fully-qualified URLs in the database.

Migration advice for Laravel teams. Do not attempt to bridge Symfony's DI into Laravel. Take three things from this repo — the entity field set (name, path, mime_type, size), the absolute-path/public-URL separation, and the named-transformation idea — and implement them with Storage, intervention/image, and a small MediaManager. You will spend less time than making the bundle load, and you will get a design that survives the next framework upgrade.

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.
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator