Files
Mark Gregory 1e06af2105
tests / ci (push) Canceled after 0s
Initial Commit
2026-10-15 16:51:54 +01:00

36 lines
3.6 KiB
PHP

<?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;
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,'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);
if($nights>30) throw ValidationException::withMessages(['check_out'=>'Maximum stay is 30 nights.']);
$available=!LodgeBooking::whereIn('status',['pending','confirmed'])->where('check_in','<',$v['check_out'])->where('check_out','>',$v['check_in'])->exists();
return view('lodge.availability',compact('v','available','nights')+['total'=>$nights*$this->nightlyRate()/100]);
}
public function book(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),'guest_name'=>'required|string|max:120','guest_email'=>'required|email|max:255','guest_phone'=>'nullable|string|max:40','notes'=>'nullable|string|max:2000']);
$nights=CarbonImmutable::parse($v['check_in'])->diffInDays(CarbonImmutable::parse($v['check_out']));
if($nights>30) throw ValidationException::withMessages(['check_out'=>'Maximum stay is 30 nights.']);
// Serialized booking creation protects against concurrent requests on supported database engines.
$booking=DB::transaction(function()use($v,$nights){
DB::table('lodge_booking_locks')->where('id',1)->lockForUpdate()->first();
if(LodgeBooking::whereIn('status',['pending','confirmed'])->where('check_in','<',$v['check_out'])->where('check_out','>',$v['check_in'])->exists()) throw ValidationException::withMessages(['check_in'=>'These dates have just been booked. Please choose different dates.']);
return LodgeBooking::create($v+['reference'=>'CR31-'.strtoupper(Str::random(9)),'nights'=>$nights,'total_pence'=>$nights*$this->nightlyRate(),'status'=>'pending']);
},3);
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),'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.'); }
}