Files

124 lines
4.4 KiB
PHP

<?php
declare(strict_types=1);
namespace Modules\RestaurantDelivery\DTOs;
use Modules\RestaurantDelivery\Enums\DeliveryStatus;
use Modules\RestaurantDelivery\Models\Delivery;
readonly class DeliveryTrackingDTO
{
public function __construct(
public int $deliveryId,
public string $trackingCode,
public DeliveryStatus $status,
public ?RiderLocationDTO $riderLocation = null,
public ?float $pickupLatitude = null,
public ?float $pickupLongitude = null,
public ?float $dropLatitude = null,
public ?float $dropLongitude = null,
public ?string $pickupAddress = null,
public ?string $dropAddress = null,
public ?int $eta = null,
public ?float $remainingDistance = null,
public ?string $routePolyline = null,
public ?array $riderInfo = null,
public ?array $timeline = null,
) {}
/**
* Create DTO from Delivery model and Firebase data.
*/
public static function fromDeliveryAndFirebase(Delivery $delivery, ?array $firebaseData = null): self
{
$riderLocation = null;
if ($firebaseData && isset($firebaseData['rider_location'])) {
$riderLocation = RiderLocationDTO::fromArray($firebaseData['rider_location']);
}
return new self(
deliveryId: $delivery->id,
trackingCode: $delivery->tracking_code,
status: $delivery->status,
riderLocation: $riderLocation,
pickupLatitude: (float) $delivery->pickup_latitude,
pickupLongitude: (float) $delivery->pickup_longitude,
dropLatitude: (float) $delivery->drop_latitude,
dropLongitude: (float) $delivery->drop_longitude,
pickupAddress: $delivery->pickup_address,
dropAddress: $delivery->drop_address,
eta: $firebaseData['eta'] ?? null,
remainingDistance: $firebaseData['remaining_distance'] ?? null,
routePolyline: $firebaseData['route']['polyline'] ?? $delivery->route_polyline,
riderInfo: $delivery->rider ? [
'id' => $delivery->rider->id,
'name' => $delivery->rider->full_name,
'phone' => $delivery->rider->phone,
'photo' => $delivery->rider->photo_url,
'rating' => $delivery->rider->rating,
'vehicle_type' => $delivery->rider->vehicle_type,
] : null,
timeline: $delivery->statusHistory?->map(fn ($h) => [
'status' => $h->to_status,
'timestamp' => $h->changed_at->toIso8601String(),
'label' => DeliveryStatus::from($h->to_status)->label(),
])->toArray(),
);
}
/**
* Convert to array.
*/
public function toArray(): array
{
return [
'delivery_id' => $this->deliveryId,
'tracking_code' => $this->trackingCode,
'status' => $this->status->value,
'status_label' => $this->status->label(),
'status_description' => $this->status->description(),
'status_color' => $this->status->color(),
'rider_location' => $this->riderLocation?->toArray(),
'pickup' => [
'latitude' => $this->pickupLatitude,
'longitude' => $this->pickupLongitude,
'address' => $this->pickupAddress,
],
'drop' => [
'latitude' => $this->dropLatitude,
'longitude' => $this->dropLongitude,
'address' => $this->dropAddress,
],
'eta' => $this->eta,
'eta_formatted' => $this->eta ? $this->formatEta($this->eta) : null,
'remaining_distance' => $this->remainingDistance,
'remaining_distance_formatted' => $this->remainingDistance
? round($this->remainingDistance, 1).' km'
: null,
'route_polyline' => $this->routePolyline,
'rider' => $this->riderInfo,
'timeline' => $this->timeline,
];
}
/**
* Format ETA in human-readable format.
*/
protected function formatEta(int $minutes): string
{
if ($minutes < 60) {
return "{$minutes} min";
}
$hours = floor($minutes / 60);
$mins = $minutes % 60;
if ($mins === 0) {
return "{$hours} hr";
}
return "{$hours} hr {$mins} min";
}
}