PHPackages                             maccesar/laravel-dropzone-enhanced - 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. maccesar/laravel-dropzone-enhanced

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

maccesar/laravel-dropzone-enhanced
==================================

Enhanced Dropzone.js component for Laravel with file upload, thumbnails, and photo management

v2.7.0(2mo ago)1115↓100%MITPHPPHP ^7.4|^8.0|^8.2

Since Apr 12Pushed 2mo ago1 watchersCompare

[ Source](https://github.com/macCesar/laravel-dropzone-enhanced)[ Packagist](https://packagist.org/packages/maccesar/laravel-dropzone-enhanced)[ RSS](/packages/maccesar-laravel-dropzone-enhanced/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (8)Dependencies (3)Versions (39)Used By (0)

Laravel Dropzone Enhanced
=========================

[](#laravel-dropzone-enhanced)

[![Latest Version on Packagist](https://camo.githubusercontent.com/15e19efdf494a635ce4904460c04726b7403e06f2e62449d14f513e0cc878017/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6d616363657361722f6c61726176656c2d64726f707a6f6e652d656e68616e6365642e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/maccesar/laravel-dropzone-enhanced)[![Total Downloads](https://camo.githubusercontent.com/ee295a288ceb64511ea22e1d79b068bbee495f132d57ea26fc2cbd431164a3a5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6d616363657361722f6c61726176656c2d64726f707a6f6e652d656e68616e6365642e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/maccesar/laravel-dropzone-enhanced)[![License](https://camo.githubusercontent.com/6c75fcd330c196b8fde5c7d9e55d5b301a6b4ae582d46d03ceb56807181604d0/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6d616363657361722f6c61726176656c2d64726f707a6f6e652d656e68616e6365642e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/maccesar/laravel-dropzone-enhanced)

A powerful and customizable Laravel package that enhances Dropzone.js to provide an elegant and efficient image upload and management solution for your Eloquent models.

Features
--------

[](#features)

- **Seamless Integration**: Add a complete image management UI to your models with a single trait and two Blade components.
- **Standalone &amp; Dependency-Free**: Works out-of-the-box with no need for external libraries like Glide.
- **Automatic Thumbnail Generation**: Natively processes and creates thumbnails for fast-loading galleries.
- **Central Thumbnail Cache**: All generated thumbnails stored in a single `cache/` directory — clean up everything with one command or `rm -rf storage/app/public/cache`.
- **Full Management UI**: Includes drag &amp; drop reordering, main image selection, lightbox preview with navigation, and secure deletion.
- **Highly Customizable**: Configure everything from image dimensions and quality to storage disks and route middleware.
- **Smart URL Generation**: Automatic relative URL generation that works consistently across all environments (local, staging, production) without `.env` configuration hassles.
- **Handy Helpers**: `src`/`srcset` helpers on models and photos (including raw storage paths) for quick, optimized URLs.
- **Upload-time Image Warming**: Pre-generate all desired thumbnail sizes at upload time via `warmSizes`, `warmFactor`, and `warmFormat` props — no separate artisan warm commands needed for new uploads.
- **Broad Compatibility**: Supports Laravel 8, 9, 10, 11, and 12.

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

[](#requirements)

- PHP 7.4 or higher
- Laravel 8.0 or higher
- **ext-exif** (for automatic image orientation correction)
- ext-gd (for image processing)

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

[](#installation)

**1. Install via Composer**

```
composer require maccesar/laravel-dropzone-enhanced
```

**2. Run the Installer**This command publishes the config file, migrations, and assets.

```
php artisan dropzoneenhanced:install
```

**Installation Options:**

- **Interactive mode (default)**: Prompts to run migrations
- **Non-interactive mode**: Runs migrations automatically without prompts ```
    php artisan dropzoneenhanced:install --no-interaction
    ```

Note: The legacy alias `dropzone-enhanced:install` still works.

**3. Run Migrations** (skip if using `--no-interaction`)

```
php artisan migrate
```

**4. Link Storage**Ensure your public storage disk is linked so images are accessible.

```
php artisan storage:link
```

EXIF Orientation Support
------------------------

[](#exif-orientation-support)

The package automatically corrects image orientation based on EXIF data from mobile photos:

- **Auto-detection**: Reads EXIF orientation data from uploaded images
- **Smart correction**: Applies rotation/flipping to both original and thumbnails
- **Fallback handling**: Gracefully handles images without EXIF data
- **Performance optimized**: Only processes JPEG images with orientation data

### Requirements for EXIF Support

[](#requirements-for-exif-support)

- PHP `ext-exif` extension enabled
- JPEG images with EXIF metadata

Images will display correctly oriented regardless of how they were captured on mobile devices.

---

URL Generation
--------------

[](#url-generation)

### Relative vs Absolute URLs

[](#relative-vs-absolute-urls)

The package can optionally use **relative URLs** (e.g., `/storage/images/photo.jpg`) instead of absolute URLs (e.g., `http://localhost:8000/storage/images/photo.jpg`). This provides several benefits:

- ✅ **Environment agnostic**: Works seamlessly across local, staging, and production without configuration changes
- ✅ **No APP\_URL conflicts**: You can keep `APP_URL` in your `.env` file without it affecting image URLs
- ✅ **Better performance**: Relative URLs are lighter and load faster
- ✅ **CDN friendly**: Easier to integrate with CDNs and reverse proxies

### Configuration

[](#configuration)

Control URL generation behavior in `config/dropzone.php`:

```
'images' => [
    // Use relative URLs (e.g., /storage/...) instead of absolute URLs (e.g., http://localhost:8000/storage/...)
    // This prevents issues with APP_URL in .env and makes URLs work across different environments
    'use_relative_urls' => true, // Default: false (disabled for backward compatibility)
],
```

**Note**: This feature is disabled by default to maintain backward compatibility with existing installations.

### Enabling Relative URLs

[](#enabling-relative-urls)

To enable relative URLs (recommended for most use cases):

**Step 1: Publish or update your config**

```
php artisan vendor:publish --tag=dropzoneenhanced-config --force
```

**Step 2: Enable the feature in config/dropzone.php**

```
'images' => [
    'use_relative_urls' => true,
],
```

**Step 3: Clear config cache**

```
php artisan config:clear
```

**Option 2: Generate absolute URLs on-demand**

```
// For a specific photo
$relativeUrl = $photo->getUrl(); // /storage/images/photo.jpg
$absoluteUrl = url($photo->getUrl()); // http://yourdomain.com/storage/images/photo.jpg

// In Blade templates

```

### Migration from Previous Versions

[](#migration-from-previous-versions)

If you're upgrading from v2.1.7 or earlier:

- **No action required**: Existing installations continue working with absolute URLs (default behavior)
- **Optional but recommended**: Enable relative URLs for better portability:
    1. Republish the config: `php artisan vendor:publish --tag=dropzoneenhanced-config --force`
    2. Set `'use_relative_urls' => true` in `config/dropzone.php`
    3. Clear config cache: `php artisan config:clear`
- **If you had workarounds**: Once relative URLs are enabled, you can remove any workarounds like commenting out `APP_URL` in `.env`

---

Browser Pre-Resize Quality
--------------------------

[](#browser-pre-resize-quality)

When `pre_resize` is enabled, the package resizes images **in the browser before uploading** to avoid sending unnecessarily large files to the server. The maximum dimensions are controlled by `default_dimensions` in config.

Unlike Dropzone's built-in resize (which uses default canvas interpolation producing aliased, pixelated results), this package uses a custom `transformFile` hook that sets `imageSmoothingQuality = 'high'` on the canvas context, enabling **Lanczos/bicubic interpolation** in modern browsers — the same quality you'd get from Photoshop or ImageMagick.

### Configuration

[](#configuration-1)

```
// config/dropzone.php
'images' => [
    'pre_resize'         => true,          // Resize in browser before upload
    'default_dimensions' => '1386x2100',   // Max width × height to keep
    'quality'            => 100,           // JPEG output quality (0–100)
],
```

### How it works

[](#how-it-works)

```
Original photo: 4032×3024 (12 MB)
       ↓  Browser resizes with bicubic interpolation
Upload to server: 1386×1039 (sharp, ~400 KB)
       ↓  Server generates thumbnails from this
Storefront: 462×346 webp, 924×693 webp, 1386×1039 webp

```

Setting `pre_resize: false` uploads the original file (useful when you need the full resolution on the server).

---

Thumbnail Cache Management
--------------------------

[](#thumbnail-cache-management)

All generated thumbnails are stored in a **central `cache/` directory** at the root of your storage disk. This makes cache management simple — no need to hunt down `thumbnails/` folders scattered throughout your storage tree.

### Storage Structure

[](#storage-structure)

```
storage/app/public/
├── cache/                          ← All generated thumbnails here
│   └── products/
│       └── 16/
│           ├── 462x700/
│           │   ├── abc123.webp     ← WebP variant
│           │   └── abc123.jpg      ← JPG variant (if requested)
│           ├── 924x1400/
│           │   └── abc123.webp
│           └── 96x145/
│               └── abc123.webp
└── products/
    └── 16/
        └── abc123.jpg               ← Original images (untouched)

```

The format belongs in the file extension — no redundant `_webp` suffix in folder names. All format variants of the same dimensions share the same folder.

### Clearing Thumbnails

[](#clearing-thumbnails)

**Via Artisan command:**

```
# With confirmation prompt
php artisan dropzoneenhanced:clear-thumbnails

# Skip confirmation (useful for deploy scripts)
php artisan dropzoneenhanced:clear-thumbnails --force

# Use a specific disk
php artisan dropzoneenhanced:clear-thumbnails --disk=s3 --force
```

**Directly on the server:**

```
rm -rf storage/app/public/cache
```

After clearing, thumbnails regenerate on demand when pages are accessed, or you can pre-generate them with your own warm-up command.

### Configuration

[](#configuration-2)

```
// config/dropzone.php
'storage' => [
    'disk' => 'public',
    'directory' => 'images',
    'thumbnail_cache_path' => 'cache',  // Customize the cache directory name
],
```

### Upgrading from v2.4.x

[](#upgrading-from-v24x)

Previous versions stored thumbnails in `thumbnails/` subfolders next to each original photo (e.g., `products/16/thumbnails/462x700_webp/`). After upgrading to v2.5.0, old thumbnails are no longer served. Regenerate them:

```
# Remove old scattered thumbnails (optional but recommended)
find storage/app/public -type d -name "thumbnails" -exec rm -rf {} + 2>/dev/null; true

# Pre-generate in new central location
php artisan products:warm-images   # or your equivalent command
```

---

Quickstart: A Practical Example
-------------------------------

[](#quickstart-a-practical-example)

This guide shows the most common use case: managing photos for an existing model in an edit form.

### Step 1: Prepare Your Model

[](#step-1-prepare-your-model)

Add the `HasPhotos` trait to any Eloquent model you want to associate with images.

```
// app/Models/Product.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use MacCesar\LaravelDropzoneEnhanced\Traits\HasPhotos;

class Product extends Model
{
  use HasPhotos;

  // ... your other model properties
}
```

### Step 2: Implement the View

[](#step-2-implement-the-view)

In your Blade view (e.g., `resources/views/products/edit.blade.php`), add the two components. They work together to provide the full experience.

```
{{-- resources/views/products/edit.blade.php --}}

@extends('layouts.app')

@section('content')
  Edit Product: {{ $product->name }}

    @csrf
    @method('PUT')

    {{-- Your other form fields --}}

      Product Name

    {{-- 1. UPLOAD NEW PHOTOS --}}
    Add New Photos

    {{-- 2. MANAGE EXISTING PHOTOS --}}
    Manage Existing Photos
    Drag to reorder, click the star to set the main photo, or use the trash icon to delete.

    Save Changes

@endsection
```

### How It Works

[](#how-it-works-1)

- The `` component provides the Dropzone interface to upload new images, which are automatically associated with the same `$product`.
- The `` component displays the gallery of already uploaded images for the given `$product`, enabling management actions (reorder, delete, set main).

---

Component Reference
-------------------

[](#component-reference)

### Uploader: ``

[](#uploader-x-dropzone-enhancedarea-)

This component provides the file upload interface.

ParameterTypeDescriptionDefault`:model``Model`**Required.** The Eloquent model instance to attach photos to.`directory``string`**Required.** The subdirectory within your storage disk to save the images.`dimensions``string`Max dimensions for resize (e.g., "1920x1080").`config('dropzone.images.default_dimensions')``preResize``bool`Whether to resize the image in the browser before upload. Set `false` to preserve original quality.`config('dropzone.images.pre_resize')``maxFiles``int`Maximum number of files allowed to be uploaded.`config('dropzone.images.max_files')``maxFilesize``int`Maximum file size in MB.`config('dropzone.images.max_filesize')``reloadOnSuccess``bool`If `true`, the page will automatically reload after all uploads are successfully completed.`false``keepOriginalName``bool`If `true`, store files using the sanitized original filename (adds numeric suffix on collisions).`false``locale``string`Assign uploaded photos to a locale (requires multilingual enabled).`null``warmSizes``string[]`Array of dimension strings to pre-generate immediately at upload time (e.g. `['462', '1200x675']`).`config('dropzone.images.warm_sizes')``warmFactor``int`Multiplier for warm generation: `2` = 1× + 2× per size. Range: 1–5.`config('dropzone.images.warm_factor')``warmFormat``string`Output format for warmed thumbnails: `webp`, `jpg`, or `png`.`config('dropzone.images.warm_format')`Example: keep original filenames in a custom directory

```

```

### Upload-time Image Warming

[](#upload-time-image-warming)

By default, thumbnails are generated **on demand** the first time a view calls `$photo->src('462', 'webp')`. This causes a brief delay on first render. The `warmSizes`, `warmFactor`, and `warmFormat` props instruct the component to pre-generate all desired sizes **immediately at upload time**, so images load instantly from the very first request.

```

{{-- Generates 8 thumbnails synchronously:
     1200x675@1x, 2400x1350@2x
     800x450@1x,  1600x900@2x
     416x234@1x,  832x468@2x
     80x80@1x,    160x160@2x  --}}
```

**Dimension syntax** — identical to `src()` / `srcset()`:

- Width-only (`'462'`) → height inferred from original aspect ratio
- WxH (`'1200x675'`) → exact dimensions

**Setting app-wide defaults** in `config/dropzone.php`:

```
'images' => [
    'warm_sizes'  => ['462', '96'],    // generated for every upload
    'warm_factor' => 2,
    'warm_format' => 'webp',
],
```

**Performance**: generation is synchronous within the upload request. For 3–5 sizes × factor 2–3 (6–15 thumbnails) the added time is typically &lt; 3 seconds — an acceptable trade-off versus slow first-render on-demand generation. If you need truly async warming, dispatch a queued job in your model observer after upload.

---

### Gallery: ``

[](#gallery-x-dropzone-enhancedphotos-)

This component displays and manages existing photos for a model. The view action opens a built-in lightbox with prev/next navigation and keyboard support.

ParameterTypeDescriptionDefault`:model``Model`**Required.** The Eloquent model instance whose photos you want to display.`thumbnailDimensions``string`Thumbnail size (e.g. `200x200`, `400x300`).`config('dropzone.images.thumbnails.dimensions')``locale``string`Filter photos by locale (requires multilingual enabled).`null`---

Advanced Usage
--------------

[](#advanced-usage)

### Working with the `HasPhotos` Trait

[](#working-with-the-hasphotos-trait)

The trait adds several useful methods to your model:

```
// Get all associated photos as a Collection (ordered by sort_order)
$product->photos;

// Get the main photo model instance
$photo = $product->mainPhoto();

// Get the URL of the main photo (original)
$url = $product->getMainPhotoUrl();

// Get the URL of the main photo with processing options
// Signature: getMainPhotoUrl($dimensions, $format, $quality, $cropPosition)
$squareUrl   = $product->getMainPhotoUrl('400x400');                   // Square 400x400
$webpUrl     = $product->getMainPhotoUrl('800x600', 'webp');           // WebP format
$qualityUrl  = $product->getMainPhotoUrl('400x400', 'jpg', 85);        // Custom quality
$topCropUrl  = $product->getMainPhotoUrl('400x400', 'webp', 90, 'top'); // Custom crop position

// Get the thumbnail URL of the main photo (default dimensions from config)
$thumbUrl = $product->getMainPhotoThumbnailUrl();
$thumbUrlTop = $product->getMainPhotoThumbnailUrl('400x400', 'top'); // Override crop position

// Alternatively, get the main photo model and call getUrl() directly
$mainPhoto = $product->mainPhoto();
$customUrl = $mainPhoto?->getUrl('400x400'); // Square 400x400
$webpUrl   = $mainPhoto?->getUrl('800x600', 'webp'); // WebP format
$qualityUrl = $mainPhoto?->getUrl('400x400', 'jpg', 85); // Custom quality
$topCropUrl = $mainPhoto?->getUrl('400x400', 'webp', 90, 'top'); // Custom crop position

// Set a specific photo as the main one
$product->setMainPhoto($photoId);

// Check if the model has any photos
if ($product->hasPhotos()) {
  // ...
}

// Delete all photos associated with the model
$product->deleteAllPhotos();

// Quick helpers (NEW)
$product->src('300'); // Main photo, width-only; keeps aspect ratio
$product->src('300x300', 'webp', null, 'top'); // Main photo with top crop
$product->srcset('300x200', 3, 'jpg'); // 1x/2x/3x srcset for main photo
$photo->src('400'); // Specific Photo model, width-only
$photo->src('400x400', 'webp', null, 'bottom'); // Specific Photo model with bottom crop
$photo->srcset('400x300', 2, 'webp'); // Srcset for a Photo model
$product->srcFromPath('clients/avatar/main-photo.jpg', '300', 'webp'); // Any storage path
$product->srcsetFromPath('clients/avatar/main-photo.jpg', '300x300', 3, 'jpg'); // Srcset from storage path
```

### Image Helper Cheatsheet

[](#image-helper-cheatsheet)

These helpers work with the `HasPhotos` trait and the `Photo` model.

- **Main photo shortcuts (trait)**```
    ```

$model-&gt;src('300'); // width-only; keeps aspect ratio; uses mainPhoto(), fallback to first $model-&gt;srcset('300x200', 3); // 1x/2x/3x with the given dimensions

```

- **Photo instance shortcuts**
```php
$photo->src('400');                // width-only; keeps aspect ratio from the original
$photo->srcset('400x300', 2, 'jpg'); // srcset 1x/2x in JPG

```

- **Raw storage paths (no relation needed)**```
    ```

$model-&gt;srcFromPath('clients/avatar/main-photo.jpg', '320', 'webp'); $model-&gt;srcsetFromPath('clients/avatar/main-photo.jpg', '320x320', 3, 'jpg'); $model-&gt;srcFromPath('clients/avatar/main-photo.jpg', '320x320', 'webp', null, null, 'top'); // Crop from top

```

Notes:
- If you pass width-only (`'300'`), height is inferred from the original aspect ratio; if it cannot be inferred, you get the original URL as 1x.
- Respects `dropzone.storage.disk`, `dropzone.images.thumbnails.*`, and `use_relative_urls`.
- `crop_position` is configurable globally (`config('dropzone.images.thumbnails.crop_position')`) and can be overridden per-call (e.g., `'top'`, `'bottom'`, `'left'`, `'right'`, `'top-left'`, etc.).
- Internally uses `mainPhoto()` and falls back to the first photo when none is marked as main.

### Advanced Customization Examples

#### Custom Upload Controller

Create a custom controller to extend the package's functionality:

```php
