Using the excellent tinify-php in a Filament Laravel application
Originally posted
Real code
Personal project
Laravel
Filament
After running Google lighthouse on one of my sites I found a lot of images were oversized and needed a way to resize and compress on upload. I found that tinypng.com has an api which works with all file types and is free for 500 images a month.
First get an API key from https://tinypng.com/developers and then install the package via composer:
composer require tinify/tinify
Then in the Filament model resource make sure we have a file upload field and use Filaments built in features for forcing a resize on upload.
Forms\Components\FileUpload::make('photo')
->image()
->imageEditor()
->imageResizeMode('contain')
->imageResizeTargetWidth('550')
Then in our create and edit resource pages we have a function which handles the upload to tinify:
// Before we save and create the record we want to change the form data
protected function mutateFormDataBeforeCreate(array $data): array
{
$data = $this->transformImages($data);
return $data;
}
protected function transformImages(array $data): array
{
$localFilepath = storage_path('app/public/' . $data['photo']); // Local path to the uploaded image
try {
// Set the Tinify API Key from environment variable
\Tinify\setKey(env('TINIFY_API_KEY'));
// Optimize the image with Tinify
$source = \Tinify\fromFile($localFilepath);
// Get the file extension and generate the new file name
$ext = pathinfo($localFilepath, PATHINFO_EXTENSION);
$fileName = Str::slug($data['name'], '-') . '-by-' . Str::slug($data['artist']) . '.' . $ext;
// Save the optimized image temporarily to a local file
$tinifiedLocalFile = storage_path('app/public/' . $fileName); // Temporary location for the Tinified image
$source->toFile($tinifiedLocalFile);
// Store the tinified image into DigitalOcean Spaces using the 'do' disk
$filePathInDO = Storage::disk('do')->putFileAs('stitchalongs', new \Illuminate\Http\File($tinifiedLocalFile), $fileName, 'public');
// Remove the temporary tinified file after upload
if (file_exists($tinifiedLocalFile)) {
unlink($tinifiedLocalFile);
}
// Update the 'photo' field in the $data array to point to the new location in DigitalOcean Spaces
$data['photo'] = $filePathInDO;
} catch (\Tinify\AccountException $e) {
// Handle the error if the API key is invalid or account limit is reached
Notification::make()
->title('Account Error')
->body($e->getMessage())
->danger()
->send();
} catch (\Tinify\ClientException $e) {
// Handle the error if there's a problem with the source image or request options
Notification::make()
->title('Client Error')
->body($e->getMessage())
->danger()
->send();
} catch (\Tinify\ServerException $e) {
// Handle the error if Tinify API has server-side issues
Notification::make()
->title('Server Error')
->body($e->getMessage())
->danger()
->send();
} catch (\Tinify\ConnectionException $e) {
// Handle network connection errors
Notification::make()
->title('Connection Error')
->body($e->getMessage())
->danger()
->send();
} catch (\Exception $e) {
// Handle any other errors
Notification::make()
->title('Unexpected Error')
->body($e->getMessage())
->danger()
->send();
}
// Always return the data array, even if an error occurred
return $data;
}