drop_off_points (with SPATIAL INDEX on coordinates) +
drop_off_capacity_logs. Public nearby query via ST_Distance_Sphere
returns DOPs sorted by distance with distance_meters in payload.
Admin CRUD plus capacity-log endpoint. Household creation auto-assigns
to the nearest active DOP within 25km; resident can re-run the lookup
via POST /households/{uuid}/reassign-drop-off.
97 feature tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
63 lines
1.9 KiB
PHP
63 lines
1.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\DropOff;
|
|
|
|
use App\Models\DropOffPoint;
|
|
use Illuminate\Support\Collection;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class DropOffPointFinder
|
|
{
|
|
/**
|
|
* Return active DOPs within $radiusKm of the given coordinates,
|
|
* ordered by distance ascending. Each result has a `distance_meters`
|
|
* attribute attached.
|
|
*
|
|
* @return Collection<int, DropOffPoint>
|
|
*/
|
|
public function nearby(float $latitude, float $longitude, float $radiusKm = 5.0, int $limit = 25): Collection
|
|
{
|
|
$radiusMeters = $radiusKm * 1000;
|
|
|
|
$rows = DB::table('drop_off_points')
|
|
->whereNull('deleted_at')
|
|
->where('status', DropOffPoint::STATUS_ACTIVE)
|
|
->select('id')
|
|
->selectRaw(
|
|
'ST_Distance_Sphere(coordinates, ST_SRID(POINT(?, ?), 4326)) AS distance_meters',
|
|
[$longitude, $latitude],
|
|
)
|
|
->whereRaw(
|
|
'ST_Distance_Sphere(coordinates, ST_SRID(POINT(?, ?), 4326)) <= ?',
|
|
[$longitude, $latitude, $radiusMeters],
|
|
)
|
|
->orderBy('distance_meters')
|
|
->limit($limit)
|
|
->get();
|
|
|
|
if ($rows->isEmpty()) {
|
|
return collect();
|
|
}
|
|
|
|
$distancesById = $rows->pluck('distance_meters', 'id');
|
|
$points = DropOffPoint::with('barangay')
|
|
->whereIn('id', $rows->pluck('id'))
|
|
->get()
|
|
->keyBy('id');
|
|
|
|
return $rows->map(function ($r) use ($points, $distancesById) {
|
|
$p = $points->get($r->id);
|
|
if ($p) {
|
|
$p->distance_meters = (float) $distancesById->get($r->id);
|
|
}
|
|
|
|
return $p;
|
|
})->filter()->values();
|
|
}
|
|
|
|
public function nearest(float $latitude, float $longitude, float $maxKm = 25.0): ?DropOffPoint
|
|
{
|
|
return $this->nearby($latitude, $longitude, $maxKm, 1)->first();
|
|
}
|
|
}
|