'immutable_datetime', 'arrival_date' => 'immutable_datetime', 'auto_update' => 'boolean', ]; protected $appends = [ 'departure_date_display', 'departure_time_display', 'arrival_date_display', 'arrival_time_display', 'arrival_day_difference', 'duration', 'duration_display', 'distance', ]; public function calculateGreatCircleDistance(): float{ $earthRadiusKm = 6371; [$depLat, $depLong] = [$this->departureAirport->latitude_deg, $this->departureAirport->longitude_deg]; [$arrLat, $arrLong] = [$this->arrivalAirport->latitude_deg, $this->arrivalAirport->longitude_deg]; $latDelta = deg2rad($arrLat - $depLat); $longDelta = deg2rad($arrLong - $depLong); $a = sin($latDelta / 2) * sin($latDelta / 2) + cos(deg2rad($depLat)) * cos(deg2rad($arrLat)) * sin($longDelta / 2) * sin($longDelta / 2); $c = 2 * atan2(sqrt($a), sqrt(1 - $a)); return $earthRadiusKm * $c; } protected function departureDateDisplay(): Attribute { return Attribute::make( get: fn() => $this->departure_date ->toMutable() ->setTimezone($this->departureAirport->timezone) ->format('j M Y') ); } protected function departureTimeDisplay(): Attribute { return Attribute::make( get: fn() => $this->departure_date ->toMutable() ->setTimezone($this->departureAirport->timezone) ->format('g:iA') ); } protected function arrivalDateDisplay(): Attribute { return Attribute::make( get: fn() => $this->arrival_date ?->copy() ->setTimezone($this->arrivalAirport->timezone) ->format('j M Y') ); } protected function arrivalTimeDisplay(): Attribute { return Attribute::make( get: fn() => $this->arrival_date ?->copy() ->setTimezone($this->arrivalAirport->timezone) ->format('g:iA') ); } protected function arrivalDayDifference(): Attribute { return Attribute::make( get: function () { if (!$this->arrival_date) return 0; $departureLocal = $this->departure_date->copy()->setTimezone($this->departureAirport->timezone); $arrivalLocal = $this->arrival_date->copy()->setTimezone($this->arrivalAirport->timezone); return (int) abs( Carbon::parse($arrivalLocal->toDateString()) ->diffInDays(Carbon::parse($departureLocal->toDateString())) ); } ); } protected function duration(): Attribute { return Attribute::make( get: fn() => $this->departure_date->diffInMinutes($this->arrival_date) ); } protected function durationDisplay(): Attribute { return Attribute::make( get: function () { $hours = intdiv($this->duration, 60); $minutes = $this->duration % 60; return $hours . 'h ' . str_pad($minutes, 2, '0', STR_PAD_LEFT) . 'm'; } ); } protected function distance(): Attribute { return Attribute::make( get: fn() => $this->calculateGreatCircleDistance() ); } public function user(): BelongsTo { return $this->belongsTo(User::class); } public function departureAirport(): BelongsTo { return $this->belongsTo(Airport::class, 'departure_airport_id'); } public function arrivalAirport(): BelongsTo { return $this->belongsTo(Airport::class, 'arrival_airport_id'); } public function crewType(): BelongsTo { return $this->belongsTo(CrewType::class); } public function airline(): BelongsTo { return $this->belongsTo(Airline::class); } public function aircraft(): BelongsTo { return $this->belongsTo(Aircraft::class); } public function seatType(): BelongsTo { return $this->belongsTo(SeatType::class); } public function flightClass(): BelongsTo { return $this->belongsTo(FlightClass::class); } public function flightReason(): BelongsTo { return $this->belongsTo(FlightReason::class); } }