@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers;
|
||||
use App\Models\LodgeBooking;
|
||||
use App\Models\LodgePhoto;
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
@@ -8,7 +9,7 @@ use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
class LodgeController extends Controller {
|
||||
private function nightlyRate(): int { return max(1,(int)config('lodge.nightly_rate_pence',17500)); }
|
||||
public function home() { return view('lodge.home',['rate'=>$this->nightlyRate()/100]); }
|
||||
public function home() { return view('lodge.home',['rate'=>$this->nightlyRate()/100,'uploadedPhotos'=>LodgePhoto::latest()->get()]); }
|
||||
public function availability(Request $request) {
|
||||
$v=$request->validate(['check_in'=>'required|date|after_or_equal:today','check_out'=>'required|date|after:check_in','guests'=>'required|integer|min:1|max:'.config('lodge.max_guests',6)]);
|
||||
$start=CarbonImmutable::parse($v['check_in']);$end=CarbonImmutable::parse($v['check_out']);$nights=$start->diffInDays($end);
|
||||
@@ -29,6 +30,6 @@ class LodgeController extends Controller {
|
||||
return redirect()->route('lodge.confirmation',['reference'=>$booking->reference]);
|
||||
}
|
||||
public function confirmation(string $reference) { $booking=LodgeBooking::where('reference',$reference)->firstOrFail();return view('lodge.confirmation',compact('booking')); }
|
||||
public function admin(Request $request) { abort_unless($request->user()?->email===config('lodge.admin_email') && filled(config('lodge.admin_email')),403);return view('lodge.admin',['bookings'=>LodgeBooking::latest()->paginate(30)]); }
|
||||
public function admin(Request $request) { abort_unless($request->user()?->email===config('lodge.admin_email') && filled(config('lodge.admin_email')),403);return view('lodge.admin',['bookings'=>LodgeBooking::latest()->paginate(30),'photos'=>LodgePhoto::latest()->get()]); }
|
||||
public function update(Request $request,LodgeBooking $booking) {abort_unless($request->user()?->email===config('lodge.admin_email') && filled(config('lodge.admin_email')),403);$v=$request->validate(['status'=>'required|in:pending,confirmed,cancelled']);if($v['status']!=='cancelled' && LodgeBooking::whereKeyNot($booking->id)->whereIn('status',['pending','confirmed'])->where('check_in','<',$booking->check_out)->where('check_out','>',$booking->check_in)->exists()) throw ValidationException::withMessages(['status'=>'Date conflict.']);$booking->update($v);return back()->with('success','Booking updated.'); }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?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.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user