diff --git a/app/Services/PostcodeService.php b/app/Services/PostcodeService.php index e05af9d..b093789 100644 --- a/app/Services/PostcodeService.php +++ b/app/Services/PostcodeService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Models\Postcode; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Log; @@ -33,7 +34,7 @@ class PostcodeService } $result = match (true) { - $this->isFullPostcode($query) => $this->lookupPostcode($query), + $this->isFullPostcode($query) => $this->lookupLocalPostcode($query) ?? $this->lookupPostcode($query), $this->isOutcode($query) => $this->lookupOutcode($query), default => $this->lookupPlace($query), }; @@ -55,6 +56,34 @@ class PostcodeService return (bool) preg_match('/^[A-Z]{1,2}[0-9][0-9A-Z]?$/i', $query); } + private function lookupLocalPostcode(string $postcode): ?LocationResult + { + $normalised = strtoupper(preg_replace('/\s+/', '', $postcode)); + + $row = Postcode::find($normalised); + + if ($row === null) { + return null; + } + + return new LocationResult( + query: $postcode, + displayName: $this->formatPostcode($normalised), + lat: $row->lat, + lng: $row->lng, + ); + } + + private function formatPostcode(string $normalised): string + { + // Insert the single space before the last 3 chars ("SW1A1AA" -> "SW1A 1AA"). + if (strlen($normalised) < 5) { + return $normalised; + } + + return substr($normalised, 0, -3).' '.substr($normalised, -3); + } + private function lookupPostcode(string $postcode): ?LocationResult { $normalised = strtoupper(preg_replace('/\s+/', '', $postcode)); diff --git a/tests/Unit/Services/PostcodeServiceTest.php b/tests/Unit/Services/PostcodeServiceTest.php index 2417240..4d4025b 100644 --- a/tests/Unit/Services/PostcodeServiceTest.php +++ b/tests/Unit/Services/PostcodeServiceTest.php @@ -1,5 +1,6 @@ toBeNull(); }); + +// --- Local DB (full postcode) --- + +it('resolves a full postcode from local DB without calling HTTP', function (): void { + Postcode::create([ + 'postcode' => 'SW1A1AA', + 'outcode' => 'SW1A', + 'lat' => 51.501009, + 'lng' => -0.141588, + ]); + + Http::fake(); // any HTTP call will be recorded + + $result = $this->service->resolve('SW1A 1AA'); + + expect($result)->toBeInstanceOf(LocationResult::class) + ->and($result->displayName)->toBe('SW1A 1AA') + ->and($result->lat)->toBe(51.501009) + ->and($result->lng)->toBe(-0.141588); + + Http::assertNothingSent(); +});