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

v4.0.0(3w ago)1244MITPHPPHP ^8.2

Since Apr 12Pushed 3w 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 2d ago

READMEChangelog (10)Dependencies (9)Versions (47)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 server-configured thumbnail sizes for new uploads.
- **Supported Versions**: Supports Laravel 12 and 13.

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

[](#requirements)

- PHP 8.2 or higher
- Laravel 12 or 13
- **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
```

**5. Configure authorization**

The component places the Eloquent model class, ID, directory, and locale in a signed upload URL. No model registry is required, and modifying any of those values invalidates the request.

For private uploads and photo management, define the Gate in your application's authorization provider. Those operations are denied when the Gate is absent or returns `false`:

```
use Illuminate\Support\Facades\Gate;
use MacCesar\LaravelDropzoneEnhanced\Models\Photo;

Gate::define('dropzone.manage-photos', function (
  $user,
  string $action,
  $model,
  ?Photo $photo = null
): bool {
  return $model->user_id === $user->id;
});
```

The action is one of `upload`, `delete`, `set-main`, `view-main-status`, `reorder`, or `update-locale`.

#### Public upload forms

[](#public-upload-forms)

Visitors can upload without registering when the application opts in explicitly:

```
'routes' => [
  'middleware' => ['web', 'throttle:20,1'],
],

'security' => [
  'allow_public_uploads' => true,
],
```

The component still creates a signed URL containing the server-selected model, directory, and locale. Visitors cannot alter that context. MIME, size, pixel, file-count, and warm-thumbnail limits remain active, and `user_id` is stored as `null`. This option bypasses the Gate only for `upload`; delete, reorder, main-photo, status, and locale operations remain denied without authorization.

### Upgrading from 2.x

[](#upgrading-from-2x)

This is a security-focused major upgrade:

1. Upgrade the host application to PHP 8.2+ and Laravel 12 or 13.
2. Merge the new `routes` and `security` keys into the published config.
3. Define the authorization Gate, or explicitly enable `allow_public_uploads`for upload-only public forms.
4. Remove `warmSizes`, `warmFactor`, and `warmFormat` component props; configure their snake-case equivalents under `images` instead.
5. Keep throttle protection when removing `auth` for a public upload form. The package always applies `signed` directly to its upload route.
6. If uploader IDs are UUIDs/ULIDs or use a custom users table, configure the `database` section before running the package migration.

The previous access-key, public-delete, convention-based ownership, and arbitrary model-class behaviors have been removed. Management requests now require an explicit Gate decision; public upload is a separate opt-in.

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

# Keep specific dimensions, delete everything else (crop variants like
# 640x360_top of a kept dimension are preserved too)
php artisan dropzoneenhanced:clear-thumbnails --keep=640x360,960x540 --force
```

`--keep` is the tool for standardizing sizes: after changing the dimensions your app requests, old dimension directories become orphans that nothing will ever serve again — list the sizes you actually use and everything else is removed.

**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
],
```

### Upscale Protection

[](#upscale-protection)

By default, thumbnails are **never generated larger than the original image**. Requesting `1920x1080` from a 640px-wide photo would produce an interpolated upscale that weighs more than the original and looks worse — instead, the request is clamped to the largest real crop at the requested aspect ratio (`640x360` in this example). Both `src()`/`srcset()` and warm generation honor the clamp, and `srcset()` drops multiplier entries that collapse to an already-listed variant.

If your app genuinely needs upscaled variants:

```
// config/dropzone.php
'images' => [
    'thumbnails' => [
        'allow_upscale' => true,  // default: false
    ],
],
```

### Thumbnail URL Caching

[](#thumbnail-url-caching)

Resolved thumbnail URLs are stored in Laravel's cache to skip a `Storage::exists()` check per request. With the `file` cache driver **every cached URL costs one inode** (one photo × several dimensions × formats adds up fast), which can exhaust file-count quotas on shared hosting. If that's your case, disable it — the generated image files themselves are unaffected:

```
// config/dropzone.php
'images' => [
    'thumbnails' => [
        'cache_urls' => false,  // default: true
    ],
],
```

### Upgrading from v3.0.x: `cache/` → `.cache/`

[](#upgrading-from-v30x-cache--cache)

**Breaking:** the default `thumbnail_cache_path` changed from `cache` to `.cache` (hidden directory, harder to confuse with app data). If you kept the old default, either set `'thumbnail_cache_path' => 'cache'` explicitly in your published config, or migrate the directory:

```
# Move existing thumbnails to the new location
php artisan dropzoneenhanced:migrate-cache-path --force

# Or just drop the old directory and let thumbnails regenerate on demand
php artisan dropzoneenhanced:migrate-cache-path --delete --force

# Custom directories
php artisan dropzoneenhanced:migrate-cache-path --from=cache --to=.cache --disk=public
```

### 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`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')`. Configure warm sizes on the server to generate them immediately after upload. Request data and component props cannot override these limits.

**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'],
    'warm_factor' => 2,
    'warm_format' => 'webp',
],

'security' => [
    'max_warm_sizes' => 10,
    'max_width' => 12000,
    'max_height' => 12000,
    'max_pixels' => 40000000,
],
```

**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.

### Upload Preview Thumbnails

[](#upload-preview-thumbnails)

The client-side previews Dropzone shows while files are queued are configurable via the `previews` block in `config/dropzone.php`:

```
'previews' => [
    'display_width'    => 180,   // preview box width in px
    'display_height'   => 180,   // preview box height in px
    'thumbnail_width'  => 576,   // generated thumbnail width (larger than the box for crisp retina previews)
    'thumbnail_height' => 576,   // generated thumbnail height
    'thumbnail_method' => 'crop', // 'crop' or 'contain'
],
```

The generated thumbnail is intentionally larger than the visible box so previews stay sharp on high-density displays.

---

### 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

The bundled upload route requires its signed model/directory context. Replacing it
with an unsigned custom route removes that protection. Prefer model observers or a
separate image-processing service after upload. If a custom controller is required,
preserve `signed`, `web`, throttle, and the selected private/public authorization mode.

#### Multiple Upload Areas for Different Photo Types

Handle different image categories for the same model:

```blade
{{-- Main product gallery --}}

Product Gallery

{{-- Technical specifications images --}}

Technical Specifications

{{-- Thumbnail/avatar images --}}

Product Thumbnails

```

#### Working with Photo Data

[](#working-with-photo-data)

Access and manipulate photo metadata:

```
// Get photo information
$photo = $product->photos->first();

echo $photo->filename;           // UUID filename
echo $photo->original_filename;  // Original upload name
echo $photo->extension;          // File extension
echo $photo->mime_type;          // MIME type
echo $photo->size;               // File size in bytes
echo $photo->width;              // Image width
echo $photo->height;             // Image height
echo $photo->sort_order;         // Display order
echo $photo->is_main;            // Boolean main status

// Get URLs
echo $photo->getUrl();                    // Original image URL
echo $photo->getThumbnailUrl();           // Default thumbnail (from config)
echo $photo->getPath();                   // Storage path

// Custom image processing (NEW in v2.1)
echo $photo->getUrl('400x400');           // Square 400x400
echo $photo->getUrl('800x600', 'webp');   // Rectangular WebP
echo $photo->getUrl('400x400', 'jpg', 85); // Custom quality
echo $photo->getUrl('300x200', 'png');    // PNG format

// Photo operations
$photo->deletePhoto();  // Delete photo and files
```

#### Custom Photo Filtering and Sorting

[](#custom-photo-filtering-and-sorting)

Add custom scopes to filter photos:

```
// Create a custom Photo model extending the package's Photo
