Module 2 — Geographic Data: PSGC tables (regions/provinces/cities/ barangays) with native geometry(polygon|point, 4326) columns; cascading dropdown endpoints; ST_Contains-based GPS resolution; service-area CRUD with barangay attach/detach; SamplePsgcSeeder + psgc:import command. Module 3 — User Management: 5 role-specific profile tables (driver/ helper/scanner/store_partner with verification_status + rejection reason); profile auto-created on register; admin user CRUD with filters/pagination, suspend/activate, profile approve/reject; self-service /me + /me/profile with role-aware validation that blocks self-approval and resets rejected profiles to pending on resubmit. 69 feature tests passing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
54 lines
1.7 KiB
PHP
54 lines
1.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Geo;
|
|
|
|
use App\Models\Barangay;
|
|
use Illuminate\Support\Facades\Cache;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
class GeoLocationService
|
|
{
|
|
public function __construct(private readonly int $cacheTtlSeconds = 300) {}
|
|
|
|
/**
|
|
* Resolve a (latitude, longitude) pair to the containing barangay using
|
|
* MySQL ST_Contains. Returns null if no barangay polygon contains the point
|
|
* or if the input is outside the Philippines bounding box.
|
|
*/
|
|
public function findBarangayByCoordinates(float $latitude, float $longitude): ?Barangay
|
|
{
|
|
if (! $this->insidePhilippinesBoundingBox($latitude, $longitude)) {
|
|
return null;
|
|
}
|
|
|
|
$key = sprintf('geo:resolve:%.6f:%.6f', $latitude, $longitude);
|
|
|
|
$barangayId = Cache::remember($key, $this->cacheTtlSeconds, function () use ($latitude, $longitude) {
|
|
$row = DB::table('barangays')
|
|
->whereNull('deleted_at')
|
|
->whereNotNull('boundary')
|
|
->whereRaw(
|
|
'ST_Contains(boundary, ST_SRID(POINT(?, ?), 4326))',
|
|
[$longitude, $latitude],
|
|
)
|
|
->select('id')
|
|
->first();
|
|
|
|
return $row?->id;
|
|
});
|
|
|
|
if (! $barangayId) {
|
|
return null;
|
|
}
|
|
|
|
return Barangay::with('cityMunicipality.province.region')->find($barangayId);
|
|
}
|
|
|
|
private function insidePhilippinesBoundingBox(float $latitude, float $longitude): bool
|
|
{
|
|
// Rough bounds for the Philippines archipelago.
|
|
return $latitude >= 4.5 && $latitude <= 21.5
|
|
&& $longitude >= 116.0 && $longitude <= 127.0;
|
|
}
|
|
}
|