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

Laravel Model Media Laravel Package

dialloibrahima/laravel-model-media

A Laravel package for attaching media files to Eloquent models. Upload, store, and associate images or documents with your records through a simple, fluent API, keeping media handling clean and organized. Ideal for galleries, avatars, and file attachments.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install the package:
composer require dialloibrahima/laravel-model-media
  1. Look first at the package README and src/ directory. Since this is a small package (11 stars, no dependents), the trait source is the most reliable API reference. Confirm the exact trait namespace, config keys, and method names before wiring it into a model.

  2. Publish any package config if available:

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

Check config/model-media.php or similar for the default disk, upload directory, and visibility settings.

  1. Add the media column(s) to your migration. The package stores paths as ordinary model attributes, so no extra media table is needed:
Schema::table('users', function (Blueprint $table) {
    $table->string('avatar')->nullable();
});
  1. Add the trait to the model and declare which attributes are media-backed:
use Dialloibrahima\ModelMedia\Traits\HasMedia; // verify namespace in README

class User extends Model
{
    use HasMedia;

    protected $mediaAttributes = ['avatar']; // check exact property name
}
  1. First use case: a user avatar upload. The simplest flow is to accept an uploaded file in a controller, store it on the configured disk, and assign the relative path to the model attribute. Then expose a URL accessor or API resource field rather than returning the raw path.

The package is best suited for simple “one file per attribute” needs: avatars, covers, thumbnails, PDFs, and similar single-file attachments where you do not need a full media library.

Implementation Patterns

Single-file attributes. Treat each media column as a nullable string containing a relative path. Let the package handle storage concerns, but keep the model attribute as the source of truth. This keeps queries simple and avoids joins.

Replacing files. The common day-to-day pattern is replace-and-cleanup:

public function updateAvatar(Request $request, User $user)
{
    $request->validate([
        'avatar' => ['required', 'image', 'max:2048'],
    ]);

    // Exact method names depend on the package; the pattern is:
    // 1. delete or overwrite the previous file
    // 2. store the new file
    // 3. persist the new relative path
    $user->update([
        'avatar' => $request->file('avatar'),
    ]);
}

If the trait does not automatically delete the old file, do it explicitly before saving the replacement. Otherwise you will accumulate orphaned files.

URL accessors. Store relative paths, not full URLs. Generate URLs at read time so the disk or CDN can change without a data migration:

public function getAvatarUrlAttribute(): ?string
{
    return $this->avatar
        ? Storage::disk(config('model-media.disk', 'public'))->url($this->avatar)
        : null;
}

Then expose it through an API resource or $appends.

Multiple files without a media table. If you need a small gallery, use a JSON column cast to an array and let the package store each file path:

protected $casts = [
    'gallery' => 'array',
];

Loop over uploaded files in the controller and append stored paths. This is the pragmatic extension point when one attribute is not enough but a full media table is overkill.

Disk strategy. Use the public disk for locally served files and S3-compatible disks for production. Keep the disk name in config, not hard-coded in controllers. For private files, return temporary URLs rather than public ones.

Validation and form requests. Validate file type, size, and dimensions before the trait touches storage. The package manages paths, not upload security. Use Laravel’s image, mimes, max, and dimensions rules.

Testing. Use Storage::fake() in feature tests and assert that the expected path was stored on the model:

Storage::fake('public');

$response = $this->post('/avatar', [
    'avatar' => UploadedFile::fake()->image('avatar.jpg'),
]);

$this->assertNotNull($user->fresh()->avatar);
Storage::disk('public')->assertExists($user->fresh()->avatar);

Cleanup on delete. Add a model observer or deleting event to remove associated files. Because there is no media table, the model is the only record of what exists on disk, so deletion logic must be explicit.

Gotchas and Tips

  • No metadata table means no rich media features. You cannot easily query by file type, order files, track dimensions, or store arbitrary metadata. If you need conversions, responsive images, or multiple named collections, use Spatie Media Library or a dedicated media model instead.
  • Orphaned files are the main operational risk. Replacing an avatar or deleting a model will not necessarily delete the old file. Verify the trait’s boot/event behavior and add explicit cleanup. A scheduled command that compares disk contents against model paths is a useful safety net.
  • Do not store full URLs. Store relative paths. Full URLs break when you switch from local to S3, add a CDN, or change the APP_URL.
  • Public disk symlink. If using the public disk locally, run php artisan storage:link. Missing symlinks are a common “file uploaded but 404” cause.
  • Filename collisions and path safety. Confirm how the package generates filenames. Prefer hashed or UUID-based names over original client filenames. Never trust user-supplied paths.
  • Config caching. If you publish config, remember php artisan config:clear during development. Config changes are invisible while cached.
  • Mass assignment. Do not expose the media path attribute directly to mass assignment from user input. Accept an UploadedFile, not a string path. Whitelist media attributes in $fillable only if you control the values.
  • Transactions and filesystem writes do not roll back together. If a database transaction fails after a file is stored, the file remains. Store files after validation but be prepared to clean up on failure.
  • Package maturity. With 11 stars and zero dependents, treat the source as documentation. Pin the version in composer.json, read the trait before upgrading, and avoid relying on undocumented behavior. If the package stalls, the trait is small enough to fork or replace with a local helper.
  • Extension points. The most useful customization points are the config disk/directory, a custom path generator if the trait exposes one, and model accessors for URL generation. Override trait methods in the model only after checking that the package does not call parent in a way that conflicts.
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.
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules
loco/loco
starfederation/datastar-php
yupmin/modern-phystrix
stephenjude/extended-artisan-commands
lumphp/laravel-macroable
lumphp/laravel-collections
lumphp/laravel-contracts
sermepa/sermepa
arkecosystem/fortify