idavoll/media-module
A Laravel media module for handling uploads and file storage. It lets you organize media, attach files to Eloquent models, and serve images through configurable disks and conversions, giving your app a reusable media management layer.
This is an AsgardCMS module, not a standalone package. It provides the media library (upload, folders, thumbnails, media picker) that plugs into an existing AsgardCMS install. Do not try to use it in a plain Laravel app.
Minimum steps:
composer.json in the repo and match its require block (asmcms/core, Laravel version, intervention/image) against your AsgardCMS major version. With one star and no visible maintainers, version drift is the real install risk — pin the core version in your own composer.json before adding this.composer require idavoll/media-moduleModules\Media\Providers\MediaServiceProvider; make sure the module is listed in config/modules.php (or modules_statuses.json) and enabled from /admin/modules.php artisan module:migrate Media (plain php artisan migrate works if the module's migrations are auto-loaded). You should get media__files and media__folders style tables.php artisan module:publish-config Media and php artisan module:publish Media. Verify config/media.php and the published JS/CSS exist — the admin picker silently renders nothing without them./admin/media, upload an image, create a folder, move the file. If that works, the package is wired correctly.Where to look first, in order:
config/media.php — disk, allowed extensions/mimetypes, max size, and the thumbnails array. This is 80% of day-to-day configuration.src/Repositories/FileRepository.php and its contract — the primary API surface for querying and creating files.src/Facades/Imagy.php / the Imagy service — how URLs and thumbnails are resolved.src/Http/Controllers/ — the upload endpoint the admin JS posts to, and the routes/middleware protecting it.First realistic use case: attach an existing media file to one of your own Eloquent models and render a thumbnail in a Blade view. That exercises storage, the repository, and Imagy in one pass.
Render an existing file anywhere. The File entity exposes path and filename; always resolve display URLs through Imagy rather than building paths by hand, so thumbnails and the configured disk stay consistent:
use Modules\Media\Entities\File;
$file = File::find($id);
$small = Imagy::getThumbnail($file->path, 'smallThumb');
$all = Imagy::getThumbnails($file->path); // ['smallThumb' => '...', ...]
<img src="{{ Imagy::getThumbnail($file->path, 'smallThumb') }}" alt="{{ $file->filename }}">
Wire the media picker into your own admin form. The module ships a Blade partial used across AsgardCMS admin screens. Include it alongside your normal input and let it write the file ID into a hidden field:
@include('media::admin.fields.file-picker', [
'name' => 'image',
'value' => old('image', $model->image_id ?? null),
])
The exact partial path and the required script include vary by version — copy them from the module's own resources/views/admin form views rather than guessing. Make sure the module's compiled JS is included on that page via the AsgardCMS asset stack.
Persist the relationship yourself. The module doesn't know about your models. Store a file_id column (or a morphTo if you want files attached polymorphically) and add a small accessor:
public function image() { return $this->belongsTo(File::class, 'image_id'); }
public function getImageUrlAttribute() { return $this->image ? Imagy::getThumbnail($this->image->path, 'mediumThumb') : null; }
Add custom thumbnail sizes. Don't resize at render time — declare sizes in config and let Imagy cache them:
// config/media.php
'thumbnails' => [
'smallThumb' => ['width' => 160, 'height' => 160, 'mode' => 'fit'],
'mediumThumb' => ['width' => 480, 'height' => 480, 'mode' => 'fit'],
'heroShot' => ['width' => 1400, 'height' => 600, 'mode' => 'crop'],
],
Hook into uploads with events. For anything async (search indexing, S3 replication, EXIF extraction), listen rather than modify the module. The module fires file lifecycle events around upload and move:
Event::listen(\Modules\Media\Events\FileWasUploaded::class, function ($event) {
ProcessUploadedFile::dispatch($event->file); // keep handlers small and idempotent
});
Extend the repository through the container. If you need a new query scope, bind your own implementation instead of editing vendor code:
$this->app->bind(
\Modules\Media\Repositories\FileRepository::class,
\App\Media\AppFileRepository::class
);
Keep the custom class extending the module's repository and only override what you need, so module upgrades stay mergeable.
Programmatic uploads. For imports or seeders, use the repository (or the upload controller's underlying service) rather than writing rows directly — it handles sanitization, disk storage, and event dispatch. Post to the upload endpoint with the session/CSRF middleware if you're driving it from your own JS.
idavoll/media-module, but classes almost certainly live under Modules\Media\... (AsgardCMS convention), autoloaded from /Modules/Media, not from vendor/. IDE autocomplete often misses it — add the path to your PHPStan/IDE config.File::cursor() and calls Imagy::getThumbnails($file->path). This alone prevents most "site feels slow after deploy" tickets.public/media/thumbnails or the configured thumbnails_path) or version filenames and append a cache-buster to the URL.Imagy delegates to intervention/image; the driver is configured in config/image.php (gd vs imagick), not in media.php. If you installed only GD, crop/fit on large images can fail on memory — raise memory_limit or switch drivers.max_size units. Check whether the config value is kilobytes or bytes for your version; the validation message and the actual limit can disagree, which produces confusing "file too large" errors on small uploads.photo.jpg will get suffixed names depending on version — never assume the stored filename matches the original, and always read $file->filename, not the name you posted.media.php, but URLs are built through the filesystem config. A missing storage:link, or a private disk with no url key, yields valid DB rows and broken images with no error. Verify php artisan storage:link and that the disk's url is set.storage/logs/laravel.log and the disk's permissions before suspecting the code.File row does not reliably remove the physical file or the generated thumbnails, and deleting your own model leaves the file_id pointing at a live file. Schedule a periodic reconciliation job: compare DB rows to disk contents (and vice versa) and clean up.packages/ directory with a VCS repository entry in composer.json, so composer update doesn't overwrite your patch.composer.lock and pin the AsgardCMS core tag. An unpinned composer update that bumps core can silently break the module's views, routes, or migration ordering, and you'll have no upstream to ask.How can I help you explore Laravel packages today?