nedarta / yii2-upload-behavior
Configurable Yii2 image upload behavior with variants (resize, thumbnail, smartcrop) and automatic cleanup.
Installs: 24
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:yii2-extension
pkg:composer/nedarta/yii2-upload-behavior
Requires
- php: >=7.4
- nedarta/yii2-autocrop: ^1.0
- yiisoft/yii2: ^2.0
- yiisoft/yii2-imagine: ^2.2
This package is auto-updated.
Last update: 2025-11-26 15:23:02 UTC
README
Full-featured Yii2 image upload behavior with advanced processing capabilities.
Features
- Automatic file uploads via
UploadedFile - Nested directory creation with proper permissions
- EXIF auto-rotation - automatically corrects image orientation from cameras/phones
- Multiple image variants with flexible processing options:
- Resize (maintain aspect ratio)
- Thumbnail (crop to exact dimensions)
- Smart crop (intelligent cropping with optional nedarta/yii2-smart-cropper)
- Copy (fallback option)
- Dependency-based variant pipeline - create variants from other variants
- Format conversion - force output to JPG, PNG, or WebP
- Automatic cleanup - removes old variants on update and delete
- Random filename generation - prevents naming conflicts
Requirements
- Yii2
- yii2-imagine extension
- PHP GD or Imagick extension
- PHP EXIF extension (for auto-rotation)
Installation
Install via Composer:
composer require nedarta/yii2-upload-behavior
The extension will automatically install the required dependency yiisoft/yii2-imagine.
Configure the @upload alias in your application config:
'aliases' => [ '@upload' => '@webroot/upload', ],
Basic Usage
Simple Configuration
use nedarta\behaviors\UploadBehavior; class Event extends \yii\db\ActiveRecord { public $upload; // Virtual attribute for file upload public function rules() { return [ [['upload'], 'file', 'extensions' => 'png, jpg, jpeg', 'maxSize' => 1024 * 1024 * 10], ]; } public function behaviors() { return [ [ 'class' => UploadBehavior::class, 'uploadAttribute' => 'upload', 'imageAttribute' => 'image', 'uploadAlias' => '@upload/images/event', ], ]; } }
Form Example
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data']]); ?> <?= $form->field($model, 'upload')->fileInput() ?> <div class="form-group"> <?= Html::submitButton('Upload', ['class' => 'btn btn-success']) ?> </div> <?php ActiveForm::end(); ?>
Controller Example
public function actionCreate() { $model = new Event(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return $this->redirect(['view', 'id' => $model->id]); } return $this->render('create', ['model' => $model]); }
Configuration Options
Basic Properties
| Property | Type | Default | Description |
|---|---|---|---|
uploadAttribute |
string | 'upload' |
Virtual attribute that receives the UploadedFile |
imageAttribute |
string | 'image' |
Database attribute where filename is stored |
uploadAlias |
string | required | Yii alias for storage folder |
baseName |
string|Closure | 'image' |
Base filename before random number |
autoRotate |
bool | true |
Automatically rotate images based on EXIF orientation |
forceConvert |
string|null | null |
Force output format: 'jpg', 'png', 'webp', 'jpeg' |
variants |
array | ['' => ['resize' => [2500, 2500]]] |
Variant definitions |
Base Name with Closure
'baseName' => function ($model) { return 'event-' . $model->id; },
EXIF Auto-Rotation
By default, images are automatically rotated based on their EXIF orientation data (common with phone cameras). To disable:
'autoRotate' => false,
Note: Auto-rotation only works with JPEG files and requires the PHP EXIF extension.
Format Conversion
Force all images (original and variants) to a specific format:
'forceConvert' => 'jpg', // Converts all uploads to JPG
Supported formats: jpg, jpeg, png, webp
Image Variants
Simple Variants
'variants' => [ '' => ['resize' => [2500, 2500]], // Original (no prefix) 'thumb_' => ['thumbnail' => [300, 300]], // Square thumbnail 'medium_' => ['resize' => [800, 600]], // Medium size ],
This creates:
image-1234.jpg(original, resized to max 2500x2500)thumb_image-1234.jpg(300x300 thumbnail)medium_image-1234.jpg(resized to max 800x600)
Variant Options
Resize
Maintains aspect ratio, scales down to fit within dimensions:
'variant_' => ['resize' => [1920, 1080], 'quality' => 85]
Thumbnail
Crops to exact dimensions:
'variant_' => ['thumbnail' => [400, 400], 'quality' => 80]
Smart Crop
Intelligent cropping (requires nedarta/yii2-smart-cropper):
'variant_' => ['smartcrop' => [600, 400], 'quality' => 85]
Copy
Simply copies the source image:
'variant_' => [] // Empty config = copy
Dependency Pipeline
Create variants from other variants for optimized processing:
'variants' => [ '' => ['resize' => [2500, 2500]], // Step 1: Resize original 'large_' => ['resize' => [1920, 1920], 'dependsOn' => ''], // Step 2: From original 'thumb_' => ['smartcrop' => [300, 300], 'dependsOn' => 'large_'], // Step 3: From large 'mini_' => ['thumbnail' => [100, 100], 'dependsOn' => 'thumb_'], // Step 4: From thumb ],
Benefits:
- Faster processing (work from smaller images)
- Better quality chain
- Efficient resource usage
Advanced Examples
Blog Post with Multiple Variants
public function behaviors() { return [ [ 'class' => UploadBehavior::class, 'uploadAttribute' => 'upload', 'imageAttribute' => 'image', 'uploadAlias' => '@upload/images/blog', 'baseName' => function ($model) { return Inflector::slug($model->title); }, 'forceConvert' => 'webp', 'variants' => [ '' => ['resize' => [2000, 2000]], 'hero_' => ['resize' => [1920, 1080], 'dependsOn' => '', 'quality' => 90], 'card_' => ['smartcrop' => [600, 400], 'dependsOn' => 'hero_'], 'thumb_' => ['thumbnail' => [300, 200], 'dependsOn' => 'card_'], ], ], ]; }
Product Image with Watermark Pipeline
'variants' => [ '' => ['resize' => [3000, 3000]], 'display_' => ['resize' => [1200, 1200], 'dependsOn' => ''], 'thumb_' => ['thumbnail' => [400, 400], 'dependsOn' => 'display_'], 'micro_' => ['thumbnail' => [150, 150], 'dependsOn' => 'thumb_'], ],
User Avatar
public function behaviors() { return [ [ 'class' => UploadBehavior::class, 'uploadAttribute' => 'avatar_file', 'imageAttribute' => 'avatar', 'uploadAlias' => '@upload/avatars', 'baseName' => function ($model) { return 'user-' . $model->id; }, 'forceConvert' => 'jpg', 'variants' => [ '' => ['thumbnail' => [800, 800]], 'thumb_' => ['thumbnail' => [200, 200], 'dependsOn' => ''], 'icon_' => ['thumbnail' => [64, 64], 'dependsOn' => 'thumb_'], ], ], ]; }
Displaying Images
In Views
// Original <?= Html::img('@web/upload/images/event/' . $model->image) ?> // Variant <?= Html::img('@web/upload/images/event/thumb_' . $model->image) ?> // With Yii2 helper use yii\helpers\Url; <img src="<?= Url::to('@web/upload/images/event/' . $model->image) ?>" alt=""> <img src="<?= Url::to('@web/upload/images/event/medium_' . $model->image) ?>" alt="">
Helper Method
Add to your model:
public function getImageUrl($variant = '') { if (empty($this->image)) { return null; } $filename = $variant . $this->image; return Yii::getAlias('@web/upload/images/event/' . $filename); } // Usage in view: <?= Html::img($model->getImageUrl()) ?> <?= Html::img($model->getImageUrl('thumb_')) ?>
File Structure Example
After uploading with variants, your directory will look like:
@upload/images/event/
├── event-1234.jpg (original)
├── large_event-1234.jpg (large variant)
├── thumb_event-1234.jpg (thumbnail variant)
└── mini_event-1234.jpg (mini variant)
How It Works
Event Flow
- Before Validation - Captures the uploaded file
- After Insert/Update - Processes the upload:
- Creates upload directories if needed
- Removes old variants if updating
- Saves uploaded file to temporary location
- Applies EXIF auto-rotation
- Converts format if
forceConvertis set - Generates all variants in dependency order
- Updates model with new filename
- Before Delete - Removes all variants
Variant Processing Pipeline
- Determine source for each variant (original or another variant)
- Process variants in order
- Apply transformation (resize/thumbnail/smartcrop/copy)
- Save with specified quality
- Use result as source for dependent variants
Troubleshooting
Images not rotating correctly
- Ensure PHP EXIF extension is installed:
php -m | grep exif - Check that images are JPEG format (EXIF only works with JPEG)
- Try setting
'autoRotate' => falseto test without rotation
Variants not created
- Check directory permissions (775 or 777)
- Verify GD or Imagick extension is installed
- Check error logs for specific errors
"Saving image in 'tmp' format is not supported"
- This is fixed in the current version
- Ensure you're using the latest UploadBehavior code
Old images not deleted
- Verify
deleteVariants()is being called - Check file permissions on upload directory
- Ensure variant prefixes match your configuration
License
Free to use and modify.
Credits
Created by nedarta