Reviewed-on: https://codeberg.org/lvl0/trip-planner/pulls/34 Co-authored-by: myrmidex <myrmidex@myrmidex.net> Co-committed-by: myrmidex <myrmidex@myrmidex.net>
111 lines
No EOL
3.4 KiB
PHP
111 lines
No EOL
3.4 KiB
PHP
<?php
|
|
|
|
namespace App\Domain\Trip\Services;
|
|
|
|
use App\Models\Trip;
|
|
use App\Models\CalendarSlot;
|
|
use Carbon\Carbon;
|
|
use Illuminate\Support\Collection;
|
|
|
|
class CalendarSlotService
|
|
{
|
|
public function createOrUpdateSlotsForTrip(Trip $trip): Collection
|
|
{
|
|
if (!$trip->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();
|
|
}
|
|
});
|
|
}
|
|
} |