spatie/image
Expressive PHP image manipulation by Spatie. Load an image, chain operations like resize/crop, rotate, greyscale, brightness, sharpen, and quality, then save. Supports common formats and integrates cleanly in Laravel or any PHP app.
Installation:
composer require spatie/image
Ensure exif PHP extension is enabled (required since v1.5.3).
First Use Case: Load and resize an image:
use Spatie\Image\Image;
Image::load('path/to/image.jpg')
->width(300)
->height(200)
->save('path/to/resized.jpg');
Key Entry Points:
Image::load(): Load an image from a file path.width(), height(), greyscale(), etc.save(): Save the manipulated image (optionally to a new path).Where to Look First:
Dynamic Thumbnail Generation:
$image = Image::load(storage_path('images/product.jpg'))
->fit(Fit::Cover, 400, 400)
->save(storage_path("images/thumbs/{$product->id}.jpg"));
Batch Processing with Laravel:
$images = Storage::files('uploads/products');
foreach ($images as $image) {
Image::load($image)
->resize(800, 600)
->save(str_replace('uploads', 'processed', $image));
}
Image Validation and Processing:
public function store(Request $request) {
$request->validate(['image' => 'required|image']);
$path = $request->file('image')->store('uploads');
Image::load(storage_path("app/{$path}"))
->resize(1024, null) // Maintain aspect ratio
->save();
// Store path in DB
}
Art Director Pattern (Laravel):
// app/ArtDirectors/ImageArtDirector.php
public function resize(Image $image, int $width, int $height) {
return $image->fit(Fit::Cover, $width, $height);
}
Queue Jobs for Heavy Processing:
// app/Jobs/ProcessImage.php
public function handle() {
Image::load($this->path)
->resize(1200, 800)
->watermark($this->watermarkPath, 0.2)
->save($this->outputPath);
}
Storage::disk('public')->put() for saving processed images.// app/Observers/ProductObserver.php
public function created(Product $product) {
if ($product->image) {
Image::load($product->image_path)
->resize(800, 600)
->save();
}
}
return response()->json([
'image_url' => Storage::url('processed/' . $product->id . '.jpg')
]);
Cache::tags(['product-images'])->put("product_{$product->id}_thumb", $thumbPath, now()->addDays(7));
Driver Conflicts:
Image::useImageDriver(ImageDriver::Gd)->load($path);
Aspect Ratio Distortion:
width() and height() ignore aspect ratio unless paired with fit().fit(Fit::Cover, $width, $height) or resize($width, $height, Fit::Constraint).Transparency Issues:
->background('white')->save('output.jpg');
EXIF Data:
orientation() relies on EXIF data, which may be missing or corrupted.->orientation(Orientation::Rotate0);
Memory Limits:
ImageDriver::Vips (lower memory usage) or process in chunks.if (!Image::isDriverAvailable(ImageDriver::Imagick)) {
throw new \RuntimeException('Imagick not available');
}
$image = Image::load($path);
\Log::info("Original: {$image->getWidth()}x{$image->getHeight()}");
$original = file_get_contents($path);
$processed = file_get_contents($outputPath);
if (!hash_equals(hash('md5', $original), hash('md5', $processed))) {
\Log::warning('Image processing altered content');
}
Default Driver:
config/spatie/image.php:
'default_driver' => ImageDriver::Gd,
Image::useImageDriver(ImageDriver::Imagick)->load($path);
Quality Settings:
80. Lower values (e.g., 20) reduce file size but degrade quality.quality(75) for a balance.Path Handling:
save() uses relative paths unless an absolute path is provided.storage_path() or public_path() for consistency:
->save(public_path('images/processed.jpg'));
Custom Drivers:
Spatie\Image\Contracts\ImageDriver for specialized needs (e.g., cloud storage).class S3ImageDriver implements ImageDriver {
public function load(string $path): void { /* ... */ }
public function save(string $path): void { /* ... */ }
// Implement all required methods
}
Macros:
Image class with custom methods:
Image::macro('addLogo', function ($logoPath, $position = 'bottom-right') {
return $this->insert($logoPath, $position, 0.2);
});
Usage:
Image::load($path)->addLogo('logo.png')->save();
Event Listeners:
Image::load($path)
->onProcess(function () {
\Log::info('Processing started');
})
->onSave(function () {
\Log::info('Processing completed');
})
->resize(800, 600)
->save();
Fallback Logic:
try {
$image = Image::load($path)->resize(800, 600);
} catch (\Spatie\Image\Exceptions\UnsupportedFormatException $e) {
\Log::warning("Unsupported format: {$e->getMessage()}");
// Fallback to a placeholder
}
$cacheKey = "image_{$product->id}_thumb";
if (!Cache::has($cacheKey)) {
Image::load($product->image_path)
->resize(300, 200)
->save($outputPath);
Cache::put($cacheKey, $outputPath, now()->addHours(1));
}
How can I help you explore Laravel packages today?