NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Require the package, then publish and run the migrations — the media table and the media_uploads table of direct-upload tickets, which stays empty unless direct uploads are on. Migrations are publish-only — the package never loads them, so a bare php artisan migrate won't create the media table until you publish it. They are published timestamped, and republishing lands on the same files. The service provider is auto-discovered:

composer require roundly-consulting/media-library-for-laravel

php artisan vendor:publish --tag="media-migrations"
php artisan migrate

The config file is optional — every key has an env-backed default. Publish it only to override:

php artisan vendor:publish --tag="media-config"

The package registers its signed streaming route for you (unless media.stream.enabled is false). Publish the route file only if you want to edit it:

php artisan vendor:publish --tag="media-routes"

The publish tags the package exposes are media-migrations, media-config, media-routes and media-translations — the SafeRemoteUrl rule’s English and Slovak messages, which load under the media:: namespace without publishing.

Upgrading from 2.0

2.1 adds the media_uploads tickets table. Publish the migrations again and migrate — it changes nothing else:

php artisan vendor:publish --tag="media-migrations"   # adds create_media_uploads_table
php artisan migrate
  • Every 2.1 feature is opt-in or keeps today’s behaviour: deduplicate_scope defaults to global, temporary_url_response_headers and mime_types.strict to false, direct_uploads.enabled to false.
  • 2.2 needs no migration: expectedOwner / expectedBucket on adoptUpload() and completeUpload() are opt-in, and TooManyPendingDirectUploads extends the DirectUploadsNotSupported thrown before, so existing catches keep working.
  • Planned for the next major: deduplicate_scope defaults to owner (set it to global to keep sharing across owners), temporary_url_response_headers to on (set it to false to keep plain presigned URLs), and adoptUpload() / completeUpload() require the expected owner (or an explicit opt-out for global media and drafts).
  • A custom url_generator keeps working for every existing call; only getTemporaryUrlWithOptions() and getTemporaryDownloadUrl() need it to implement GeneratesTemporaryUrlsWithOptions.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.