Model or dataset
qcod/laravel-imageup avatar
qcod/laravel-imageup

laravel-imageup, an Eloquent trait that handles image upload, resize and crop

Auto Image & file upload, resize and crop for Laravel eloquent model using Intervention image

767 stars62 forksPHPMIT

At a glance

What is it?
qcod/laravel-imageup is a Laravel package that adds upload, resize and crop to an Eloquent model through a single trait, hooking the Model saved event so files move to disk and the path is written back to a column automatically. Per-field options cover disk, path, dimensions, validation rules, placeholder images and hooks, and non-image file types work the same way.
Who is it for?
Adopt laravel-imageup if you want image handling attached to your Eloquent models rather than written out in every controller, and you are comfortable with the package writing to your database and deleting the previous file. Skip it if you need resizing that depends on runtime conditions such as a user-chosen crop region, since the crop options are fixed per field.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The trait, the fields and the saved event

qcod/laravel-imageup is a trait that gives you automatic upload, resize and crop for image handling with a good deal of customisation. The description in the README is accurate about the scope, which is images and files on an Eloquent model.

Installation is a Composer require:

bash
$ composer require qcod/laravel-imageup

The package registers itself automatically, and the README gives the manual alternative for config/app.php, which is QCod\ImageUp\ImageUpServiceProvider::class. The configuration file can be published with:

bash
php artisan vendor:publish --provider="QCod\ImageUp\ImageUpServiceProvider" --tag="config"

which creates config/imageup.php with all the settings.

The usage is deliberately thin. You add the HasImageUploads trait to the model and declare which columns hold images:

php
class User extends Model {
    use HasImageUploads;
    
    // assuming `users` table has 'cover', 'avatar' columns
    // mark all the columns as image fields 
    protected static $imageFields = [
        'cover', 'avatar'
    ];
}

Once the fields are marked, the upload happens automatically whenever the model is saved, because the trait hooks into the Model saved event. It updates the database with the new stored file path, and if it finds an old image it deletes that once the new one is uploaded.

That last behaviour is the one to think about before adopting. The package deletes the previous file automatically, which is convenient and also means there is no undo and no soft delete for the file. The README notes that the image field should be a VARCHAR in the database table to store the path of the uploaded image.

On the controller side, nothing special is needed. The example store method just calls User::create with the request data, and the README points out the one prerequisite of running php artisan storage:link so the images are visible from the public storage disk.

What you can configure per field

The per-field options are where this package earns its keep, and the README lists them with comments.

The width and height resize the image after upload. The crop option crops to those dimensions when true, and you can also pass an array of x and y coordinates for the crop. The disk selects where the file goes, defaulting to the config value, and the path is a folder on that disk, defaulting to the upload directory in the config.

A placeholder image covers the case where the field is empty, which matters for avatars and logos where you need something rendered rather than a broken image. The rules option takes validation rules for the upload, with the example image|max:2000, so the file type is checked and the size is capped.

The auto_upload option overrides the global setting for that one field, which is how you keep a field out of the automatic behaviour while still configuring it. The file_input option sets the name of the request file when it differs from the field name. The update_database option controls whether the field is written to the database at all, for the case where the column does not exist or you do not want the path stored.

Then there are the hooks. A before_save hook is triggered before the image is saved and an after_save hook after, with the examples given as class names, BlurFilter::class and CreateWatermarkImage::class. That is the extension point for anything the built-in options do not cover, such as blurring sensitive regions or stamping a watermark.

The model also has global properties for the disk and path used by default, with the field options overriding them per field.

Non-image files and custom filenames

The same mechanism handles files that are not images. There is a separate $fileFields array for that, and the README notes that any file type other than an image can be uploaded this way. The example is a resume field with a disk, a path of docs, validation rules of mimes:doc,pdf,docx|max:1000, an auto_upload override, a file_input of cv, and the same before_save and after_save hooks.

That the rules option takes the same shape for files as for images is the point. The validation is declared next to the field definition rather than in a controller, so the constraint on an uploaded document lives with the model that owns it.

Custom filenames are handled through a naming convention rather than a configuration array. By default the saved name is the hashName of the uploaded file, which is a random hash. To override it you add a method on the model named {fieldName}UploadFilePath:

php
class User extends Model {
    use HasImageUploads;
    
    protected static $imageFields = [
        'cover', 'avatar'
    ];
    
    // override cover file name
    protected function coverUploadFilePath($file) {
        return $this->id . '-cover-image.jpg';
    }
}

In that example the cover image is always saved as uploads/1-cover-image.jpg, using the model id. Two notes come with it: the override method must return only a relative path, and the request file is passed to it as the $file parameter.

The convention is a small piece of magic worth knowing about, since a method you write for one field is discovered by name rather than registered anywhere.

The global model properties and the config file behind them

The per-field options sit on top of a set of model-wide properties, and it is worth knowing how the two layers interact.

On the model you can declare which disk to use for uploads, with the README's example setting it to local, and the path in that disk, with uploads as the example. The README notes explicitly that both can be overridden by the field options, so the model-level values are defaults rather than fixed settings.

The auto_upload_images property is the third, and it is a boolean controlling whether uploads happen automatically at all. Its field-level counterpart, auto_upload, overrides it for one field, which is how you keep a specific field out of the automatic behaviour while still configuring how it would be handled if it were triggered manually.

Behind the model sits the published config file, config/imageup.php. The field options name their defaults as config calls, for example config('imageup.upload_disk') for the disk and config('imageup.upload_directory') for the path, and config('imageup.auto_upload_images') for the global automatic setting. That is the layer where you would change behaviour for the whole application without touching any model.

So there are three layers of configuration, and each answers a different question. The config file is the application default, the model properties are the per-model default, and the field options are the per-field override, and they win.

Two practical notes follow from the structure. The disk and path options expect a Laravel disk name and a folder within it, so the disk has to exist and be configured in filesystems.php before an upload succeeds. And the instruction to run php artisan storage:link matters for the public disk specifically, because the symlink is what lets a browser fetch the file directly rather than through PHP.

Maintenance, licence and how it compares

The release history shows a package that follows Laravel's own major versions. Version 1.2.6 is described as Laravel 13 support and was published in August 2026, version 1.2.5 came in May 2025 and 1.2.4 in May 2024, where the release notes themselves carry a small inconsistency with the tag naming.

That the package bumps for a new Laravel major is the thing to check before an upgrade. A trait that hooks the model saved event depends on Eloquent's event behaviour, and a framework major is exactly when that can shift. The version numbers are low, at 1.2.x, which is normal for a package of this size and says nothing about its usefulness.

The licence is MIT, and the repository is laid out as a normal Composer package with a composer.json, a config/ directory, src/, tests/, a phpunit.xml, a CHANGELOG.md, a CONTRIBUTING.md and a LICENSE.md. The build badge in the README points at Travis CI, which is an older CI service and suggests the test pipeline has not been modernised.

The comparison to make is with the alternative of writing the upload handling yourself in a controller or a service. That is what most applications do, and it works. The difference in approach is where the behaviour lives. Here it is on the model, so every route that saves the model gets the same handling, including routes added later by someone who never read the controller code.

The other alternative is a dedicated media library package, one of the well known Laravel media managers. Those give you conversions, multiple sizes per image, responsive sources and often a media table with its own model. That is more capability than this package offers. What this package offers instead is a much smaller surface, no extra table, and configuration that lives on your existing model as static arrays.

For a form with one avatar, the hand-written controller is less machinery. For a model with several image fields, several file types and rules that differ per field, this trait saves a lot of repetitive code.

Editorial conclusion

Adopt laravel-imageup if you want image handling attached to your Eloquent models rather than written out in every controller, and you are comfortable with the package writing to your database and deleting the previous file. Skip it if you need resizing that depends on runtime conditions such as a user-chosen crop region, since the crop options are fixed per field. Verify first by adding the trait to one model with one image column, running php artisan storage:link, saving a model with a file, and confirming both that the file lands on the intended disk and that the old file is deleted when a replacement is uploaded.

Frequently asked questions

How do I install and set up laravel-imageup?

Run composer require qcod/laravel-imageup. The package registers itself, though you can add QCod\ImageUp\ImageUpServiceProvider::class to the providers array in config/app.php. The configuration file is published with php artisan vendor:publish --provider="QCod\ImageUp\ImageUpServiceProvider" --tag="config", which creates config/imageup.php.

When does laravel-imageup upload the file?

Automatically whenever the model is saved, because the trait hooks into the Model saved event. It writes the stored file path to the database and deletes the previous image once the new one has been uploaded.

What per-field options does laravel-imageup support?

Width and height for resizing, crop which can take an x and y coordinate array, disk and path for storage location, placeholder for empty fields, rules for validation, auto_upload to override the global setting, file_input when the request file name differs from the field name, update_database to control whether the path is written, and before_save and after_save hooks.

Can laravel-imageup handle files that are not images?

Yes. A separate $fileFields array on the model handles other file types with the same options, including validation rules such as mimes:doc,pdf,docx|max:1000 for a resume field.

How do I control the saved filename in laravel-imageup?

By adding a method to the model named {fieldName}UploadFilePath, for example coverUploadFilePath for the cover field, which receives the request file and must return a relative path. The default name is the hash name of the uploaded file.

Official sources

  1. Issues
  2. License: MIT
  3. qcod/laravel-imageup on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/qcod-laravel-imageup.svg)](https://hysenlabs.com/projects/qcod-laravel-imageup)

Was this useful?