PHPackages                             ibrahim-kaya/imageman - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. ibrahim-kaya/imageman

ActiveLibrary[File &amp; Storage](/categories/file-storage)

ibrahim-kaya/imageman
=====================

Professional Laravel image upload, WebP/AVIF conversion, multi-size variants and multi-disk management package

1.0.0(1mo ago)11↑2900%MITPHPPHP &gt;=8.0

Since Mar 28Pushed 1mo agoCompare

[ Source](https://github.com/ibrahim-kaya/ImageMan)[ Packagist](https://packagist.org/packages/ibrahim-kaya/imageman)[ RSS](/packages/ibrahim-kaya-imageman/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (9)Versions (2)Used By (0)

ImageMan
========

[](#imageman)

**Professional Laravel image upload, processing and multi-disk management package.**

[![Latest Version](https://camo.githubusercontent.com/6656cd5eb01f2db85dffa3f24222d930b381a0b7e4ee64f95f231a9580d307ea/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6962726168696d2d6b6179612f696d6167656d616e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ibrahim-kaya/imageman)[![Total Downloads](https://camo.githubusercontent.com/b4be27b0c586043a885b29b636bc83db1ef891324e32f6d2f5eafcca0760b8d8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6962726168696d2d6b6179612f696d6167656d616e2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ibrahim-kaya/imageman)[![Tests](https://camo.githubusercontent.com/8d22c7d2d9d7c33b0d8b71656a618b2fde929160fb0edafd12f43892bdf92fe8/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f6962726168696d2d6b6179612f496d6167654d616e2f74657374732e796d6c3f6c6162656c3d7465737473267374796c653d666c61742d737175617265)](https://github.com/ibrahim-kaya/ImageMan/actions)[![License](https://camo.githubusercontent.com/c6558db70219ba03cc93b13cbc06dc3224d88be6220b8c3762ce06efda225f0a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6962726168696d2d6b6179612f696d6167656d616e2e7376673f7374796c653d666c61742d737175617265)](LICENSE)

> 🇹🇷 [Türkçe dokümantasyon için README.tr.md dosyasına bakın](README.tr.md)

---

Features
--------

[](#features)

- **WebP &amp; AVIF conversion** — Auto-convert any uploaded image to WebP or AVIF on the fly
- **Multi-size variants** — Generate thumbnail, medium, large and custom size presets in one pass
- **Multi-disk support** — Switch between `local`, `s3`, `ftp`, `sftp`, GCS and any Flysystem driver
- **Duplicate detection** — SHA-256 hash-based deduplication with `reuse`, `throw` or `allow` modes
- **Watermarking** — Apply logo or text watermarks with configurable position and opacity
- **LQIP placeholders** — Generate base64 blur placeholders for smooth lazy-loading transitions
- **EXIF stripping** — Remove GPS coordinates and device info to protect user privacy
- **CDN integration** — Built-in URL generators for Imgix, Cloudinary, ImageKit and Cloudflare Images
- **Event system** — `ImageUploaded`, `ImageProcessed`, `ImageDeleted` events for reactive workflows
- **Queue support** — Dispatch image processing to a background queue for fast HTTP responses
- **HasImages trait** — Drop into any Eloquent model for instant upload/retrieve/delete support
- **Artisan commands** — `imageman:regenerate`, `imageman:clean`, `imageman:convert`
- **Blade directives** — `@image`, `@responsiveImage`, `@lazyImage`
- **API Resource** — Ready-made `ImageResource` for JSON API responses
- **Filament v3** — Form component and table column for the Filament admin panel
- **Laravel Nova** — Custom field for the Nova admin panel
- **Fluent API** — Chainable builder that reads like plain English

---

Requirements
------------

[](#requirements)

DependencyVersionPHP^8.1Laravel^10.0 | ^11.0Intervention Image^3.0Optional (for admin panel integrations):

- `filament/filament` ^3.0
- `laravel/nova` ^4.0

---

Installation
------------

[](#installation)

**1. Install via Composer:**

```
composer require ibrahim-kaya/imageman
```

**2. Publish the config file:**

```
php artisan vendor:publish --tag=imageman-config
```

**3. Publish and run migrations:**

```
php artisan vendor:publish --tag=imageman-migrations
php artisan migrate
```

The `imageman_images` table will be created in your database.

---

Configuration
-------------

[](#configuration)

After publishing, edit `config/imageman.php`. Every option is documented with an inline comment in the file. Key settings:

KeyDefaultDescription`disk``local`Default filesystem disk (env: `IMAGEMAN_DISK`)`path``images`Storage directory prefix (env: `IMAGEMAN_PATH`)`format``webp`Output format: `webp`, `avif`, `jpeg`, `original``webp_quality``80`WebP encoding quality (1–100)`avif_quality``70`AVIF encoding quality (1–100)`default_sizes``['thumbnail','medium']`Size presets generated for every upload`detect_duplicates``true`Enable SHA-256 duplicate detection`on_duplicate``reuse``reuse` / `throw` / `allow``queue``false`Process images in a background queue`url_generator``default``default`, `imgix`, `cloudinary`, `imagekit`, `cloudflare``generate_lqip``true`Generate blur placeholder data URIs`strip_exif``true`Strip EXIF metadata for privacy---

Basic Usage
-----------

[](#basic-usage)

### Upload an image

[](#upload-an-image)

```
use IbrahimKaya\ImageMan\ImageManFacade as ImageMan;

// From an HTTP request field
$image = ImageMan::upload($request->file('photo'))->save();

// From a remote URL
$image = ImageMan::uploadFromUrl('https://example.com/photo.jpg')->save();

echo $image->url('medium');     // Public URL for the medium variant
echo $image->url('thumbnail');  // Public URL for the thumbnail
echo $image->url();             // Main image URL
```

### Retrieve and delete

[](#retrieve-and-delete)

```
$image = ImageMan::find(1);      // Returns ?Image
$image = ImageMan::get(1);       // Returns Image or throws ImageNotFoundException

ImageMan::destroy(1);            // Deletes DB record + disk files
$image->delete();                // Same, called on the model instance
```

---

### Upload from URL

[](#upload-from-url)

The `uploadFromUrl()` method downloads the remote image and passes it through the same processing pipeline as a regular upload (WebP conversion, resizing, watermarking, etc.):

```
// Basic
$image = ImageMan::uploadFromUrl('https://example.com/photo.jpg')->save();

// Full fluent chain — all options available
$image = ImageMan::uploadFromUrl('https://cdn.example.com/banner.png', timeoutSeconds: 60)
    ->disk('s3')
    ->collection('remote')
    ->sizes(['thumbnail', 'medium', 'large'])
    ->format('avif')
    ->watermark()
    ->meta(['alt' => 'Remote banner'])
    ->save();

// Via HasImages trait — just pass the URL string instead of an UploadedFile
$post->uploadImage('https://example.com/photo.jpg', 'gallery');
$user->uploadImage('https://example.com/avatar.png', 'avatars', ['disk' => 's3', 'timeout' => 45]);
```

**Error handling:**

```
use IbrahimKaya\ImageMan\Exceptions\UrlFetchException;

try {
    $image = ImageMan::uploadFromUrl($url)->save();
} catch (UrlFetchException $e) {
    // URL not reachable, non-2xx response, or response is not an image
    Log::error('Remote image download failed', ['url' => $url, 'reason' => $e->getMessage()]);
}
```

---

Fluent API Reference
--------------------

[](#fluent-api-reference)

All methods return `$this` (except `save()` which returns an `Image` model).

MethodDescription`->disk('s3')`Override the storage disk`->collection('gallery')`Set the collection name`->sizes(['thumbnail','large'])`Choose which size presets to generate`->withOriginal()`Also keep the unmodified original file`->for($model)`Associate with an Eloquent model`->meta(['alt' => '…'])`Attach metadata (alt text, title, etc.)`->format('avif')`Override output format for this upload`->watermark()`Enable watermark (overrides config)`->noWatermark()`Disable watermark for this upload`->withLqip()`Enable LQIP generation`->withoutLqip()`Disable LQIP generation`->maxSize(2048)`Max file size in KB`->minWidth(400)`Minimum width in pixels`->maxWidth(2000)`Maximum width in pixels`->aspectRatio('16/9')`Enforce aspect ratio`->save()`Execute and return `Image` model---

Size Variants
-------------

[](#size-variants)

Define named presets in `config/imageman.php`:

```
'sizes' => [
    'thumbnail' => ['width' => 150,  'height' => 150,  'fit' => 'cover'],
    'medium'    => ['width' => 800,  'height' => 600,  'fit' => 'contain'],
    'large'     => ['width' => 1920, 'height' => 1080, 'fit' => 'contain'],
    'hero'      => ['width' => 2560, 'height' => 1440, 'fit' => 'cover'],
],
```

Retrieve by name:

```
$image->url('thumbnail');   // Thumbnail URL
$image->url('hero');        // Hero URL
$image->path('medium');     // Storage path
$image->variants();         // ['thumbnail' => ['path' => …, 'width' => 150, …], …]
$image->srcset();           // "url 150w, url 800w, url 1920w"
```

---

Disk Management
---------------

[](#disk-management)

Switch disk per upload — no config change needed:

```
// Upload to S3
$image = ImageMan::upload($file)->disk('s3')->save();

// Upload to FTP
$image = ImageMan::upload($file)->disk('ftp')->save();

// Default disk override for the next upload only
ImageMan::disk('s3')->upload($file)->save();
```

Make sure the target disk is configured in `config/filesystems.php`.

---

WebP &amp; AVIF Conversion
--------------------------

[](#webp--avif-conversion)

Change format globally (in `.env` or config):

```
# .env
IMAGEMAN_DISK=s3
```

```
// config/imageman.php
'format' => 'avif',   // Convert everything to AVIF
```

Or per upload:

```
$image = ImageMan::upload($file)->format('avif')->save();
$image = ImageMan::upload($file)->format('original')->save(); // No conversion
```

---

Watermarking
------------

[](#watermarking)

### Global config

[](#global-config)

Set a default watermark for all uploads in `config/imageman.php`:

```
'watermark' => [
    'enabled'  => true,
    'type'     => 'image',               // 'image' or 'text'
    'path'     => storage_path('app/watermark.png'),
    'text'     => null,
    'position' => 'bottom-right',
    'opacity'  => 50,
    'padding'  => 15,
],
```

### Per-upload: image watermark

[](#per-upload-image-watermark)

Override the watermark image on a single upload without touching the config file:

```
$image = ImageMan::upload($file)
    ->watermarkImage(storage_path('app/logo.png'))
    ->save();

// All options:
$image = ImageMan::upload($file)
    ->watermarkImage(
        path:     storage_path('app/logo.png'),
        position: 'bottom-right', // top-left | top-center | top-right
                                   // center-left | center | center-right
                                   // bottom-left | bottom-center | bottom-right
        opacity:  40,              // 0 (invisible) – 100 (solid)
        padding:  15,              // px gap from edge
    )
    ->save();
```

### Per-upload: text watermark

[](#per-upload-text-watermark)

```
$image = ImageMan::upload($file)
    ->watermarkText('© 2024 My Company')
    ->save();

// All options:
$image = ImageMan::upload($file)
    ->watermarkText(
        text:     '© 2024 My Company',
        position: 'bottom-center',
        opacity:  70,
        padding:  12,
    )
    ->save();
```

### Enable / disable for a single upload

[](#enable--disable-for-a-single-upload)

```
// Enable for this upload only (uses config path/text)
$image = ImageMan::upload($file)->watermark()->save();

// Disable for this upload only (even if config has enabled: true)
$image = ImageMan::upload($file)->noWatermark()->save();
```

### Priority rules

[](#priority-rules)

Method calledResult`->watermarkImage($path)`Enables watermark, uses given image`->watermarkText($text)`Enables watermark, uses given text`->watermark()`Enables watermark, keeps config type/path/text`->noWatermark()`Disables watermark for this upload*(none)*Falls back entirely to `config/imageman.php`---

Duplicate Detection
-------------------

[](#duplicate-detection)

When the same file is uploaded twice, ImageMan checks the SHA-256 hash:

```
// config/imageman.php
'detect_duplicates' => true,
'on_duplicate'      => 'reuse',  // 'reuse' | 'throw' | 'allow'
```

ModeBehaviour`reuse`Returns the existing Image model. No new file stored.`throw`Throws `DuplicateImageException` with a reference to the existing image.`allow`Creates a new record regardless.Handle the exception:

```
use IbrahimKaya\ImageMan\Exceptions\DuplicateImageException;

try {
    $image = ImageMan::upload($file)->save();
} catch (DuplicateImageException $e) {
    $existing = $e->existingImage();
    return response()->json(['message' => 'Duplicate', 'image' => $existing]);
}
```

---

Singleton Collections
---------------------

[](#singleton-collections)

A **singleton collection** holds exactly one image per model instance. When a new image is uploaded to a singleton collection, all previously stored images in that collection for the same model are automatically deleted **after** the new image has been successfully saved — so the model is never left without an image during the transition.

### Option 1 — Config-based (always singleton)

[](#option-1--config-based-always-singleton)

List the collection names in `config/imageman.php`:

```
'singleton_collections' => ['profile_pic', 'avatar', 'cover'],
```

Any upload to those collections will automatically replace the previous one — no extra method call needed:

```
// First upload
$user->uploadImage($request->file('photo'), 'profile_pic');

// Second upload — first image is automatically deleted after this saves
$user->uploadImage($request->file('photo'), 'profile_pic');
```

### Option 2 — Per-upload with `->replaceExisting()`

[](#option-2--per-upload-with--replaceexisting)

Use the fluent method to opt in on a single upload:

```
$image = ImageMan::upload($file)
    ->for($user)
    ->collection('avatar')
    ->replaceExisting()
    ->save();
```

### Option 3 — `HasImages` trait with `replace` option

[](#option-3--hasimages-trait-with-replace-option)

```
// Explicit replace
$user->uploadImage($file, 'profile_pic', ['replace' => true]);

// Explicit keep (even if profile_pic is in singleton_collections config)
$user->uploadImage($file, 'profile_pic', ['replace' => false]);
```

### Override: keeping multiple images in a singleton collection

[](#override-keeping-multiple-images-in-a-singleton-collection)

Call `->keepExisting()` to opt out of automatic replacement even when the collection is listed in config:

```
// profile_pic is in singleton_collections but we want to keep both this time
$image = ImageMan::upload($file)
    ->for($user)
    ->collection('profile_pic')
    ->keepExisting()
    ->save();
```

### How it works

[](#how-it-works)

ScenarioResult`replaceExisting()` + model setOld images in collection deleted after new save`keepExisting()`Old images kept regardless of config`replaceExisting()` + **no model**Silently ignored (no scope to delete within)Different collection on same modelUntouchedSame collection on different model instanceUntouched> **Safety guarantee:** Old images are only deleted *after* the new image has been fully written to disk and the database record has been committed. If processing fails before that point, the old image remains intact.

---

LQIP &amp; Lazy Loading
-----------------------

[](#lqip--lazy-loading)

Access the blur placeholder:

```
$image->lqip();  // "data:image/webp;base64,/9j/4AAQ…"
```

Use in Blade with the `@lazyImage` directive:

```
@lazyImage($post->image->id, 'large', ['class' => 'lazyload w-full'])
```

Renders:

```

```

Add [lazysizes](https://github.com/aFarkas/lazysizes) to your front-end for the blur-to-sharp transition.

---

EXIF Stripping
--------------

[](#exif-stripping)

Enabled by default. Strips GPS coordinates, device model, and all other EXIF metadata before saving:

```
// config/imageman.php
'strip_exif' => true,
```

---

Upload Validation
-----------------

[](#upload-validation)

Set global rules in config:

```
'validation' => [
    'max_size'      => 10240,   // 10 MB
    'allowed_mimes' => ['image/jpeg', 'image/png', 'image/webp'],
],
```

Or apply rules fluently per upload:

```
use IbrahimKaya\ImageMan\Exceptions\ValidationException;

try {
    $image = ImageMan::upload($request->file('photo'))
        ->maxSize(5120)          // 5 MB max
        ->minWidth(400)          // At least 400px wide
        ->maxWidth(4096)
        ->aspectRatio('16/9')    // Must be widescreen
        ->save();
} catch (ValidationException $e) {
    return back()->withErrors($e->errors());
}
```

---

Model Integration (HasImages Trait)
-----------------------------------

[](#model-integration-hasimages-trait)

Add the trait to any Eloquent model:

```
use IbrahimKaya\ImageMan\Traits\HasImages;

class User extends Model
{
    use HasImages;
}

class Post extends Model
{
    use HasImages;
}
```

### Methods available

[](#methods-available)

```
// Upload
$user->uploadImage($request->file('avatar'), 'avatars');
$post->uploadImage($request->file('photo'), 'gallery', ['sizes' => ['thumbnail', 'large']]);

// Retrieve
$user->getImage('avatars');          // → ?Image (most recent)
$post->getImages('gallery');         // → Collection
$post->getAllImages();               // → Collection grouped by collection name
$user->hasImage('avatars');          // → bool

// Eloquent relationships
$user->images;                       // All images (morphMany)
$user->image;                        // Latest default image (morphOne)

// Delete
$post->deleteImages('gallery');      // Delete all gallery images + files
$post->deleteImages('*');            // Delete all collections
```

---

Event System
------------

[](#event-system)

Listen to ImageMan events in your `EventServiceProvider`:

```
use IbrahimKaya\ImageMan\Events\ImageUploaded;
use IbrahimKaya\ImageMan\Events\ImageProcessed;
use IbrahimKaya\ImageMan\Events\ImageDeleted;

protected $listen = [
    ImageUploaded::class  => [SendUploadNotification::class],
    ImageProcessed::class => [TriggerCdnWarmup::class],
    ImageDeleted::class   => [CleanupSearch::class],
];
```

Event payloads:

```
// ImageUploaded
$event->image;   // Image model
$event->model;   // Associated Eloquent model (or null)

// ImageProcessed
$event->image;    // Image model (with variants populated)
$event->variants; // ['thumbnail' => ['path' => …], …]

// ImageDeleted
$event->imageId;  // Former primary key
$event->disk;     // Disk name
$event->paths;    // Array of deleted file paths
```

---

Queue Processing
----------------

[](#queue-processing)

Enable background processing to keep upload responses fast:

```
// config/imageman.php
'queue'            => true,
'queue_connection' => 'redis',
'queue_name'       => 'images',
```

Start the worker:

```
php artisan queue:work --queue=images
```

When queue is enabled, `save()` inserts the DB record immediately (so you get an ID back) and dispatches `ProcessImageJob`. Variants are available once the job completes — listen to `ImageProcessed` to know when they are ready.

---

Artisan Commands
----------------

[](#artisan-commands)

### Regenerate variants

[](#regenerate-variants)

Re-run size generation for existing images. Useful after adding a new size preset:

```
php artisan imageman:regenerate
php artisan imageman:regenerate --size=hero          # Only the 'hero' preset
php artisan imageman:regenerate --collection=gallery # Only gallery images
php artisan imageman:regenerate --disk=s3            # Only S3 images
```

### Clean orphaned images

[](#clean-orphaned-images)

Remove images with no associated model:

```
php artisan imageman:clean              # With confirmation prompt
php artisan imageman:clean --dry-run    # Preview only (no deletions)
php artisan imageman:clean --older-than=30  # Only images older than 30 days
```

### Bulk format conversion

[](#bulk-format-conversion)

Convert stored images to a new format (e.g. after changing `config.format`):

```
php artisan imageman:convert --format=webp
php artisan imageman:convert --format=avif
php artisan imageman:convert --format=webp --dry-run
```

---

CDN Integration
---------------

[](#cdn-integration)

### Imgix

[](#imgix)

```
// config/imageman.php
'url_generator' => 'imgix',
'imgix' => [
    'domain'   => env('IMGIX_DOMAIN'),    // e.g. 'mysite.imgix.net'
    'sign_key' => env('IMGIX_SIGN_KEY'),  // optional
],
```

### Cloudinary

[](#cloudinary)

```
'url_generator' => 'cloudinary',
'cloudinary' => [
    'cloud_name' => env('CLOUDINARY_CLOUD_NAME'),
    'api_key'    => env('CLOUDINARY_API_KEY'),
    'api_secret' => env('CLOUDINARY_API_SECRET'),
],
```

### ImageKit

[](#imagekit)

```
'url_generator' => 'imagekit',
'imagekit' => [
    'url_endpoint' => env('IMAGEKIT_URL_ENDPOINT'),
    'public_key'   => env('IMAGEKIT_PUBLIC_KEY'),
    'private_key'  => env('IMAGEKIT_PRIVATE_KEY'),
],
```

### Cloudflare Images

[](#cloudflare-images)

```
'url_generator' => 'cloudflare',
'cloudflare' => [
    'account_id' => env('CF_IMAGES_ACCOUNT_ID'),
    'api_token'  => env('CF_IMAGES_API_TOKEN'),
],
```

### Custom URL Generator

[](#custom-url-generator)

Implement `UrlGeneratorContract` and reference your class:

```
'url_generator' => \App\ImageMan\MyCustomUrlGenerator::class,
```

---

Blade Directives
----------------

[](#blade-directives)

### @image — Single variant

[](#image--single-variant)

```
@image($post->image->id, 'medium', ['class' => 'rounded-xl', 'alt' => 'Post photo'])
```

Renders:

```

```

### @responsiveImage — srcset

[](#responsiveimage--srcset)

```
@responsiveImage($post->image->id, [
    'sizes' => '(max-width: 768px) 100vw, 800px',
    'class' => 'w-full',
])
```

Renders:

```

```

### @lazyImage — LQIP lazy load

[](#lazyimage--lqip-lazy-load)

```
@lazyImage($post->image->id, 'large', ['class' => 'lazyload'])
```

Renders an `` with `src` set to the LQIP placeholder and `data-src` pointing to the full image, ready for [lazysizes](https://github.com/aFarkas/lazysizes).

---

API Resource
------------

[](#api-resource)

Use in controllers to return a consistent JSON structure:

```
use IbrahimKaya\ImageMan\Resources\ImageResource;

// Single image
return ImageResource::make($image);

// Collection
return ImageResource::collection(Image::all());
```

Example JSON output:

```
{
    "id": 1,
    "url": "https://cdn.example.com/images/abc.webp",
    "variants": {
        "thumbnail": "https://cdn.example.com/images/abc_thumbnail.webp",
        "medium":    "https://cdn.example.com/images/abc_medium.webp"
    },
    "lqip": "data:image/webp;base64,UklGRk…",
    "srcset": "https://… 150w, https://… 800w",
    "width": 1920,
    "height": 1080,
    "size": 204800,
    "mime_type": "image/webp",
    "original_filename": "hero-photo.jpg",
    "collection": "gallery",
    "disk": "s3",
    "meta": { "alt": "Hero photo" },
    "created_at": "2024-01-01T00:00:00.000000Z",
    "updated_at": "2024-01-01T00:00:00.000000Z"
}
```

---

Filament v3 Integration
-----------------------

[](#filament-v3-integration)

Register the plugin in your panel provider:

```
use IbrahimKaya\ImageMan\Integrations\Filament\FilamentImageManPlugin;

public function panel(Panel $panel): Panel
{
    return $panel
        ->plugin(FilamentImageManPlugin::make());
}
```

### Form component

[](#form-component)

```
use IbrahimKaya\ImageMan\Integrations\Filament\Forms\ImageManUpload;

ImageManUpload::make('photo')
    ->collection('avatars')
    ->disk('s3')
    ->sizes(['thumbnail', 'medium'])
```

### Table column

[](#table-column)

```
use IbrahimKaya\ImageMan\Integrations\Filament\Tables\ImageManColumn;

ImageManColumn::make('photo')
    ->collection('avatars')
    ->size('thumbnail')
    ->circular()
```

---

Laravel Nova Integration
------------------------

[](#laravel-nova-integration)

```
use IbrahimKaya\ImageMan\Integrations\Nova\ImageManField;

public function fields(NovaRequest $request): array
{
    return [
        ImageManField::make('Photo')
            ->collection('avatars')
            ->disk('s3')
            ->size('medium'),
    ];
}
```

---

Private Disk Routes
-------------------

[](#private-disk-routes)

To serve images stored on a private disk through your Laravel app, enable the optional routes:

```
// config/imageman.php
'register_routes'    => true,
'route_prefix'       => 'imageman',
'route_middleware'   => ['auth'],
```

Two endpoints become available:

RouteDescription`GET /imageman/{id}/{variant?}`Proxy-stream the image through PHP`GET /imageman/{id}/{variant?}/sign`Redirect to a signed temporary URL (S3/GCS)---

Temporary Signed URLs
---------------------

[](#temporary-signed-urls)

For private disks (S3, GCS, Azure Blob):

```
// Default TTL from config (imageman.signed_url_ttl, default: 60 minutes)
$url = $image->temporaryUrl();

// Custom TTL in minutes
$url = $image->temporaryUrl(30);

// Specific variant
$url = $image->temporaryUrl(15, 'medium');
```

---

Testing
-------

[](#testing)

The package ships with an Orchestra Testbench-based test suite:

```
composer install
./vendor/bin/phpunit
```

### Testing in your own application

[](#testing-in-your-own-application)

Use `Storage::fake()` and the `fakeImageFile()` helper pattern:

```
use Illuminate\Support\Facades\Storage;
use IbrahimKaya\ImageMan\ImageManFacade as ImageMan;

public function test_user_can_upload_avatar(): void
{
    Storage::fake('s3');

    $user = User::factory()->create();
    $file = \Illuminate\Http\UploadedFile::fake()->image('avatar.jpg', 200, 200);

    $image = ImageMan::upload($file)->for($user)->collection('avatars')->disk('s3')->save();

    $this->assertNotNull($image);
    $this->assertSame('avatars', $image->collection);
    $this->assertSame('s3', $image->disk);
    Storage::disk('s3')->assertExists($image->directory . '/' . $image->filename);
}
```

---

Contributing
------------

[](#contributing)

Pull requests are welcome. Please:

1. Fork the repository and create a branch from `main`.
2. Write tests covering your change.
3. Ensure `./vendor/bin/phpunit` passes with no failures.
4. Follow PSR-12 code style.
5. Submit a pull request with a clear description of the change.

---

License
-------

[](#license)

The MIT License (MIT). See [LICENSE](LICENSE) for details.

###  Health Score

36

—

LowBetter than 82% of packages

Maintenance90

Actively maintained with recent releases

Popularity4

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity38

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/f929508275c5ec25054383133d70b12a804a513623774d46765162210669c1d7?d=identicon)[ibrahim-kaya](/maintainers/ibrahim-kaya)

---

Top Contributors

[![ibrahim-kaya](https://avatars.githubusercontent.com/u/20479749?v=4)](https://github.com/ibrahim-kaya "ibrahim-kaya (3 commits)")

---

Tags

laravels3imageresizewatermarkuploadcdnWebpavifibrahim-kaya

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ibrahim-kaya-imageman/health.svg)

```
[![Health](https://phpackages.com/badges/ibrahim-kaya-imageman/health.svg)](https://phpackages.com/packages/ibrahim-kaya-imageman)
```

###  Alternatives

[unisharp/laravel-filemanager

A file upload/editor intended for use with Laravel 5 to 10 and CKEditor / TinyMCE

2.2k3.3M74](/packages/unisharp-laravel-filemanager)[intervention/image-laravel

Laravel Integration of Intervention Image

1496.5M102](/packages/intervention-image-laravel)[mafftor/laravel-file-manager

The file manager intended for using Laravel with CKEditor / TinyMCE / Colorbox

3619.3k](/packages/mafftor-laravel-file-manager)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
