Cookbook

Image Processing Examples

Comprehensive examples of image processing with the glueful/media extension

This document provides comprehensive examples of image processing in Glueful, powered by the glueful/media extension and Intervention Image v4.

Basic Operations

Simple Resize

// Using helper function (recommended)
image($context, '/path/to/photo.jpg')
    ->resize(800, 600)
    ->save('/path/to/resized.jpg');

// Using service injection
$processor = app($context, ImageProcessorInterface::class);
$result = $processor::make('/path/to/photo.jpg')
    ->resize(800, 600)
    ->save('/path/to/resized.jpg');

Maintain Aspect Ratio

// Resize by width (height auto-calculated)
image($context, '/path/to/photo.jpg')
    ->resize(800, null)  // maintain aspect ratio
    ->save('/path/to/resized.jpg');

// Or resize by height (width auto-calculated)
image($context, '/path/to/photo.jpg')
    ->resize(null, 600)  // maintain aspect ratio
    ->save('/path/to/resized.jpg');

Quality Control

// Set JPEG quality (1-100)
image($context, '/path/to/photo.jpg')
    ->resize(800, 600)
    ->quality(85)
    ->save('/path/to/output.jpg');

// Format conversion with quality
image($context, '/path/to/photo.png')
    ->format('webp')
    ->quality(80)
    ->save('/path/to/output.webp');

Advanced Operations

Cropping and Fitting

// Crop to exact dimensions from center
image($context, '/path/to/photo.jpg')
    ->crop(400, 300, 100, 50)  // width, height, x, y
    ->save('/path/to/cropped.jpg');

// Fit to exact dimensions (covers area and may crop edges)
image($context, '/path/to/photo.jpg')
    ->fit(800, 600)
    ->save('/path/to/fitted.jpg');

Watermarking

// Add watermark to bottom-right
image($context, '/path/to/photo.jpg')
    ->watermark('/path/to/logo.png', 'bottom-right', 50)  // 50% opacity
    ->save('/path/to/watermarked.jpg');

// Different watermark position
image($context, '/path/to/photo.jpg')
    ->watermark('/path/to/logo.png', 'top-left', 75)
    ->save('/path/to/watermarked.jpg');

Custom Modifications

// Apply custom Intervention operations via modify()
image($context, '/path/to/photo.jpg')
    ->modify(function ($img) {
        // Example: adjust brightness/contrast using Intervention methods
        // $img->brightness(20)->contrast(15);
    })
    ->save('/path/to/adjusted.jpg');

Caching Examples

Basic Caching

// Cache processed image for 24 hours
$imageData = image($context, '/path/to/photo.jpg')
    ->resize(800, 600)
    ->quality(85)
    ->cached('photo-800x600', 86400)
    ->getImageData();

Cache Key Generation

// Automatic cache key based on operations
$processor = image($context, '/path/to/photo.jpg')
    ->resize(800, 600)
    ->quality(85)
    ->cached();  // Auto-generates cache key

// Custom cache key with TTL
$processor = image($context, '/path/to/photo.jpg')
    ->resize(800, 600)
    ->cached('custom-thumbnail-' . $userId, 3600);

Retrieve From Cache (and Serve)

use Glueful\Cache\CacheStore;

// Resolve cache store
/** @var CacheStore<mixed> $cache */
$cache = app($context, 'cache.store');

// Compose full cache key used by ImageProcessor::cached()
$prefix = (string) (config($context, 'image.cache.prefix', 'image_'));
$key = $prefix . 'photo-800x600';

// Try to serve from cache first
if ($cached = $cache->get($key)) {
    header('Content-Type: ' . $cached['mime_type']);
    echo $cached['image_data'];
    return; // done
}

// Cache miss: process and cache
$imageData = image($context, '/path/to/photo.jpg')
    ->resize(800, 600)
    ->quality(85)
    ->cached('photo-800x600', 86400)
    ->getImageData();

header('Content-Type: image/jpeg');
echo $imageData;

Invalidate Cached Variant

$prefix = (string) (config($context, 'image.cache.prefix', 'image_'));
$key = $prefix . 'photo-800x600';
app($context, 'cache.store')->delete($key);

// Note: cached() stores the processed result under a cache key; retrieval and // invalidation should be handled by your cache layer if needed.

Remote Image Processing

Basic Remote Processing

// Process remote image with security checks
try {
    $result = image($context, 'https://example.com/photo.jpg')
        ->resize(800, 600)
        ->quality(85)
        ->cached('remote-photo-800x600')
        ->save('/path/to/local.jpg');
} catch (\Glueful\Exceptions\BusinessLogicException $e) {
    // Handle validation/security/processing errors
    echo "Image error: " . $e->getMessage();
}

Batch Processing

// Process multiple images efficiently
$urls = [
    'https://example.com/photo1.jpg',
    'https://example.com/photo2.jpg',
    'https://example.com/photo3.jpg'
];

foreach ($urls as $index => $url) {
    try {
        image($context, $url)
            ->resize(400, 300)
            ->cached("batch-photo-{$index}")
            ->save("/path/to/batch_{$index}.jpg");
    } catch (Exception $e) {
        // Log error and continue
        error_log("Failed to process {$url}: " . $e->getMessage());
    }
}

Working with Uploads

Process File Uploads

// In controller handling file upload
public function uploadImage(Request $request): Response
{
    $uploadedFile = $request->getUploadedFiles()['image'] ?? null;
    
    if (!$uploadedFile) {
        return Response::error('No image uploaded');
    }
    
    try {
        // Process uploaded file (use fromUpload for UploadedFileInterface)
        $processedData = app($context, ImageProcessorInterface::class)
            ::fromUpload($uploadedFile)
            ->resize(1200, 800)
            ->quality(85)
            ->format('webp')
            ->cached('upload-' . uniqid())
            ->getImageData();
            
        // Save to storage
        $filename = 'processed_' . time() . '.webp';
        file_put_contents(storage_path($context, 'images/' . $filename), $processedData);
        
        return Response::success(['filename' => $filename]);
        
    } catch (Exception $e) {
        return Response::error('Image processing failed: ' . $e->getMessage());
    }
}

Generate Multiple Sizes

// Create thumbnail variants from upload
$uploadedFile = $request->getUploadedFiles()['image'];
$processor = app($context, ImageProcessorInterface::class)::fromUpload($uploadedFile);

$sizes = [
    'thumbnail' => [150, 150],
    'medium' => [400, 300],
    'large' => [800, 600],
    'original' => [1200, 900]
];

$results = [];
foreach ($sizes as $size => $dimensions) {
    $processed = $processor->clone()
        ->fit($dimensions[0], $dimensions[1])
        ->quality(85)
        ->cached("upload-{$size}-" . uniqid())
        ->getImageData();
        
    $filename = "{$size}_" . time() . '.jpg';
    file_put_contents(storage_path($context, "images/{$filename}"), $processed);
    $results[$size] = $filename;
}

Performance Optimization

Memory Efficient Processing

// Process large images efficiently
$processor = image($context, '/path/to/large-image.jpg');

// Check dimensions before processing
if ($processor->getWidth() > 4000 || $processor->getHeight() > 4000) {
    // Resize very large images first
    $processor->resize(2000, 1500);
}

// Continue with normal processing
$result = $processor
    ->resize(800, 600)
    ->quality(85)
    ->save('/path/to/output.jpg');

Streaming for Large Files

// Stream large processed images
public function serveProcessedImage(string $path): void
{
    $processor = image($context, $path)
        ->resize(1200, 800)
        ->cached();
        
    // Stream directly to output
    $processor->stream([
        'Content-Disposition' => 'inline; filename="processed.jpg"'
    ]);
}

Error Handling

Comprehensive Error Handling

try {
    $result = image($context, '/path/to/photo.jpg')
        ->resize(800, 600)
        ->quality(85)
        ->save('/path/to/output.jpg');
} catch (\Glueful\Exceptions\BusinessLogicException $e) {
    // Validation/security/processing errors (format, size, dimensions, etc.)
    error_log("Image error: " . $e->getMessage());
} catch (Exception $e) {
    // Generic errors
    error_log("Unexpected error: " . $e->getMessage());
}

Configuration Examples

Environment Variables

# Production settings
IMAGE_DRIVER=imagick
IMAGE_MAX_WIDTH=2048
IMAGE_MAX_HEIGHT=2048
IMAGE_MAX_FILESIZE=10M
IMAGE_JPEG_QUALITY=85
IMAGE_WEBP_QUALITY=80
IMAGE_CACHE_ENABLED=true
IMAGE_CACHE_TTL=86400
IMAGE_VALIDATE_MIME=true
IMAGE_CHECK_INTEGRITY=true
IMAGE_DISABLE_EXTERNAL_URLS=false
IMAGE_ALLOWED_DOMAINS=example.com,cdn.example.com

Custom Configuration in Services

use Glueful\Extensions\Media\ImageProcessor;

// Custom image processor with specific settings
$processor = new ImageProcessor(
    $imageManager,
    $cache,
    $security,
    $logger,
    [
        'optimization' => [
            'jpeg_quality' => 90,
            'webp_quality' => 85,
        ],
        'security' => [
            'allowed_domains' => ['trusted-cdn.com'],
            'validate_mime' => true,
        ],
        'cache' => [
            'enabled' => true,
            'ttl' => 3600,
            'prefix' => 'custom-images-',
        ]
    ]
);

Integration with Controllers

RESTful Image API

class ImageController extends BaseController
{
    public function resize(Request $request): Response
    {
        $this->requirePermission('images.process');
        
        $url = $request->get('url');
        $width = (int) $request->get('width', 800);
        $height = (int) $request->get('height', 600);
        $quality = (int) $request->get('quality', 85);
        
        try {
            $imageData = image($context, $url)
                ->resize($width, $height)
                ->quality($quality)
                ->cached("resize-{$width}x{$height}-q{$quality}")
                ->getImageData();
                
            return Response::success([
                'processed' => true,
                'size' => strlen($imageData),
                'data' => base64_encode($imageData)
            ]);
            
        } catch (Exception $e) {
            return Response::error('Processing failed: ' . $e->getMessage());
        }
    }
}

This completes the comprehensive examples for image processing with the glueful/media extension.