90 lines
3.6 KiB
PHP
90 lines
3.6 KiB
PHP
<?php
|
|
namespace App\Http\Controllers;
|
|
|
|
use App\Models\LodgePhoto;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Storage;
|
|
use Illuminate\Support\Str;
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
class LodgePhotoController extends Controller
|
|
{
|
|
private function authorizeAdmin(Request $request): void
|
|
{
|
|
abort_unless(filled(config('lodge.admin_email')) && $request->user()?->email === config('lodge.admin_email'), 403);
|
|
}
|
|
|
|
public function store(Request $request)
|
|
{
|
|
$this->authorizeAdmin($request);
|
|
$request->validate([
|
|
'photos' => ['required','array','min:1','max:10'],
|
|
'photos.*' => ['required','image','mimes:jpeg,png,webp','max:15360'],
|
|
]);
|
|
if (!extension_loaded('gd') || !function_exists('imagewebp')) {
|
|
throw ValidationException::withMessages(['photos' => 'PHP GD with WebP support is required. Install php8.5-gd.']);
|
|
}
|
|
foreach ($request->file('photos') as $file) {
|
|
$raw = file_get_contents($file->getRealPath());
|
|
$source = @imagecreatefromstring($raw);
|
|
unset($raw);
|
|
if (!$source) {
|
|
throw ValidationException::withMessages(['photos' => 'One image could not be decoded.']);
|
|
}
|
|
$width = imagesx($source); $height = imagesy($source);
|
|
if ($width < 1 || $height < 1 || $width * $height > 45000000) {
|
|
imagedestroy($source);
|
|
throw ValidationException::withMessages(['photos' => 'Image dimensions are not supported (maximum 45 megapixels).']);
|
|
}
|
|
$id = (string) Str::uuid();
|
|
$gallery = "lodge/gallery/{$id}.webp";
|
|
$thumbnail = "lodge/thumb/{$id}.webp";
|
|
try {
|
|
$this->saveWebp($source, $width, $height, 1600, 82, $gallery);
|
|
$this->saveWebp($source, $width, $height, 480, 77, $thumbnail);
|
|
LodgePhoto::create(['gallery_path'=>$gallery,'thumbnail_path'=>$thumbnail,'alt_text'=>'Cresta 31 lodge photograph']);
|
|
} catch (\Throwable $e) {
|
|
Storage::disk('public')->delete([$gallery,$thumbnail]);
|
|
throw $e;
|
|
} finally {
|
|
imagedestroy($source);
|
|
}
|
|
}
|
|
return back()->with('success', 'Photographs uploaded and optimised.');
|
|
}
|
|
|
|
private function saveWebp($source, int $width, int $height, int $maxWidth, int $quality, string $path): void
|
|
{
|
|
$scale = min(1, $maxWidth / $width);
|
|
$w = max(1, (int) round($width * $scale));
|
|
$h = max(1, (int) round($height * $scale));
|
|
$target = imagecreatetruecolor($w, $h);
|
|
imagefill($target, 0, 0, imagecolorallocate($target, 255, 255, 255));
|
|
imagecopyresampled($target, $source, 0, 0, 0, 0, $w, $h, $width, $height);
|
|
ob_start();
|
|
try {
|
|
if (!imagewebp($target, null, $quality)) {
|
|
throw new \RuntimeException('WebP conversion failed.');
|
|
}
|
|
$bytes = ob_get_clean();
|
|
} catch (\Throwable $e) {
|
|
ob_end_clean();
|
|
imagedestroy($target);
|
|
throw $e;
|
|
}
|
|
imagedestroy($target);
|
|
if (!Storage::disk('public')->put($path, $bytes)) {
|
|
throw new \RuntimeException('Unable to save the optimised image.');
|
|
}
|
|
}
|
|
|
|
public function destroy(Request $request, LodgePhoto $photo)
|
|
{
|
|
$this->authorizeAdmin($request);
|
|
$paths = [$photo->gallery_path, $photo->thumbnail_path];
|
|
$photo->delete();
|
|
Storage::disk('public')->delete($paths);
|
|
return back()->with('success', 'Photograph deleted.');
|
|
}
|
|
}
|