start_date || !$trip->end_date) { return collect(); } // Fresh load to avoid stale relationship data $trip->refresh(); $existingSlots = $trip->calendarSlots; $existingSlotsMap = $existingSlots->keyBy(function ($slot) { return $slot->slot_date instanceof \Carbon\Carbon ? $slot->slot_date->toDateString() : $slot->slot_date; }); $startDate = Carbon::parse($trip->start_date); $endDate = Carbon::parse($trip->end_date); $newSlots = collect(); $currentDate = $startDate->copy(); $dayNumber = 1; while ($currentDate->lte($endDate)) { $slotDate = $currentDate->toDateString(); if (!$existingSlotsMap->has($slotDate)) { $slot = CalendarSlot::create([ 'trip_id' => $trip->id, 'name' => 'Day ' . $dayNumber, 'slot_date' => $slotDate, 'datetime_start' => $currentDate->copy()->startOfDay(), 'datetime_end' => $currentDate->copy()->endOfDay(), 'slot_order' => $dayNumber, ]); $newSlots->push($slot); } else { $existingSlot = $existingSlotsMap->get($slotDate); $existingSlot->update([ 'slot_order' => $dayNumber, 'datetime_start' => $currentDate->copy()->startOfDay(), 'datetime_end' => $currentDate->copy()->endOfDay(), ]); $newSlots->push($existingSlot); } $currentDate->addDay(); $dayNumber++; } $trip->calendarSlots() ->whereNotIn('slot_date', $newSlots->pluck('slot_date')) ->delete(); return $newSlots; } public function deleteSlotsForTrip(Trip $trip): void { $trip->calendarSlots()->delete(); } /** * Calculate the slot_order for a new slot based on datetime_start * Orders chronologically by start time within the same day */ public function calculateSlotOrder(int $tripId, string $slotDate, Carbon $datetimeStart): int { $maxOrder = CalendarSlot::where('trip_id', $tripId) ->where('slot_date', $slotDate) ->where('datetime_start', '<', $datetimeStart) ->max('slot_order'); return ($maxOrder ?? -1) + 1; } /** * Recalculate slot_order for all slots on a given date * Orders by datetime_start ASC * Uses database transaction with individual updates for safety */ public function recalculateSlotOrdersForDate(int $tripId, string $slotDate): void { $slots = CalendarSlot::where('trip_id', $tripId) ->where('slot_date', $slotDate) ->orderBy('datetime_start') ->get(); if ($slots->isEmpty()) { return; } // Update each slot's order within a transaction \DB::transaction(function () use ($slots) { foreach ($slots as $index => $slot) { $slot->slot_order = $index; $slot->save(); } }); } }