Installation
composer require ahonymous/crop-imagick
Enable the bundle in config/bundles.php (Symfony) or AppKernel.php (legacy):
Ahonymous\CropImagickBundle\CropImagickBundle::class => ['all' => true],
Configuration
Add to config/packages/crop_imagick.yaml:
crop_imagick:
upload_dir: '%kernel.project_dir%/public/uploads'
allowed_formats: ['jpg', 'jpeg', 'png', 'gif']
First Use Case Create a controller to handle image uploads and cropping:
use Ahonymous\CropImagickBundle\CropImagick;
use Symfony\Component\HttpFoundation\Request;
public function cropImage(Request $request, CropImagick $cropImagick)
{
$file = $request->files->get('image');
$cropData = json_decode($request->request->get('crop'), true);
$result = $cropImagick->crop($file, $cropData);
return new JsonResponse(['path' => $result]);
}
Frontend Integration Use a library like Cropper.js to send crop coordinates via AJAX to the endpoint above.
File Upload
Handle file uploads via Symfony's File component or a form with enctype="multipart/form-data".
Crop Data Extraction
Extract crop coordinates (e.g., x, y, width, height) from frontend and send as JSON:
{
"x": 100,
"y": 50,
"width": 200,
"height": 200
}
Crop Processing
Use the CropImagick service:
$croppedPath = $cropImagick->crop($file, $cropData, [
'output_format' => 'jpg',
'quality' => 80,
]);
Response Handling Return the path to the cropped image or a redirect to the processed image:
return $this->redirect($croppedPath);
Use Symfony's File type in forms:
$builder->add('image', FileType::class, [
'label' => 'Upload Image',
'mapped' => false,
'required' => true,
]);
For AJAX submissions, validate and process the crop data manually.
For bulk cropping (e.g., admin panel):
public function batchCrop(Request $request, CropImagick $cropImagick)
{
$files = $request->files->get('images');
$results = [];
foreach ($files as $file) {
$cropData = [...] // Fetch crop data per file
$results[] = $cropImagick->crop($file, $cropData);
}
return new JsonResponse($results);
}
Override the default upload directory per request:
$customPath = $cropImagick->crop($file, $cropData, [
'upload_dir' => '/custom/path',
]);
Imagick Extension Missing
Ensure ext-imagick is installed and enabled. Test with:
php -m | grep imagick
If missing, install via:
pecl install imagick
or system package manager (e.g., apt-get install php-imagick).
File Permissions
The upload_dir must be writable by the PHP process. Set permissions:
chmod -R 775 /path/to/upload_dir
Memory Limits
Large images may exceed PHP's memory_limit. Increase it in php.ini or .htaccess:
memory_limit = 512M
Image Format Restrictions
The bundle enforces allowed_formats. To bypass (not recommended):
crop_imagick:
allowed_formats: ['*'] # Allows all formats
Check Crop Data Validate crop coordinates before processing:
if (!isset($cropData['x'], $cropData['y'], $cropData['width'], $cropData['height'])) {
throw new \InvalidArgumentException('Invalid crop data');
}
Log Errors Wrap calls in try-catch to log Imagick exceptions:
try {
$result = $cropImagick->crop($file, $cropData);
} catch (\Exception $e) {
\Log::error('Crop failed: ' . $e->getMessage());
throw $e;
}
Verify Output
Use file_exists() to confirm cropped files are saved:
if (!file_exists($croppedPath)) {
throw new \RuntimeException('Cropped file not generated');
}
Custom Filters
Extend the bundle by creating a subclass of CropImagick:
class CustomCropImagick extends CropImagick
{
public function applyFilters($path)
{
$this->applyImagickFilter($path, 'blur', '0x1');
return $path;
}
}
Register as a service in services.yaml:
services:
App\Service\CustomCropImagick:
arguments:
$uploadDir: '%crop_imagick.upload_dir%'
tags: ['crop_imagick.cropper']
Event Listeners Listen for crop events (if the bundle supports them) to add pre/post-processing:
// Example for hypothetical events
$eventDispatcher->addListener('crop_imagick.pre_crop', function ($event) {
$event->setOption('quality', 90);
});
Configuration Overrides Override bundle config dynamically:
$container->getParameterBag()->set('crop_imagick.upload_dir', '/new/path');
Cache Crop Data Store crop coordinates in the database to avoid reprocessing:
$cropData = $entityRepository->findCropData($entityId);
Use Placeholders Generate placeholders for thumbnails to reduce load:
$cropImagick->resize($file, 200, 200);
Async Processing Offload cropping to a queue (e.g., Symfony Messenger) for large batches:
$message = new CropImageMessage($file, $cropData);
$bus->dispatch($message);
How can I help you explore Laravel packages today?