All checks were successful
linter / quality (pull_request) Successful in 1m21s
security / Dependency Audit (pull_request) Successful in 1m25s
security / Static Analysis (pull_request) Successful in 1m49s
tests / ci (8.4) (pull_request) Successful in 1m23s
tests / ci (8.5) (pull_request) Successful in 1m27s
- Add towns table with town_pid, town_name, state, population, town_class, date_retired - Add AustralianState enum with label and abbreviation helpers - Add Town model with active() and search() scopes - Add SyncTowns job that paginates ArcGIS API and upserts all 1977 towns - Schedule SyncTowns to run nightly at 02:00 - Add /towns/search endpoint returning JSON suggestions filtered by name and state - Add Alpine-powered autocomplete on origin/destination fields in create form - Add state filter dropdown in journeys card header to narrow autocomplete results
77 lines
2.4 KiB
PHP
77 lines
2.4 KiB
PHP
<?php
|
|
|
|
namespace App\Jobs;
|
|
|
|
use App\Models\Town;
|
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
|
use Illuminate\Foundation\Queue\Queueable;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class SyncTowns implements ShouldQueue
|
|
{
|
|
use Queueable;
|
|
|
|
private const API_URL = 'https://services-ap1.arcgis.com/ypkPEy1AmwPKGNNv/arcgis/rest/services/Town_Point/FeatureServer/0/query';
|
|
|
|
private const PAGE_SIZE = 1000;
|
|
|
|
public int $timeout = 300;
|
|
|
|
public function handle(): void
|
|
{
|
|
$offset = 0;
|
|
$synced = 0;
|
|
|
|
do {
|
|
$response = Http::timeout(30)->get(self::API_URL, [
|
|
'outFields' => 'town_pid,town_name,state,population,town_class,date_retired',
|
|
'where' => '1=1',
|
|
'f' => 'json',
|
|
'resultOffset' => $offset,
|
|
'resultRecordCount' => self::PAGE_SIZE,
|
|
'orderByFields' => 'objectid ASC',
|
|
]);
|
|
|
|
if ($response->failed()) {
|
|
Log::error('SyncTowns: API request failed', ['offset' => $offset, 'status' => $response->status()]);
|
|
break;
|
|
}
|
|
|
|
$features = $response->json('features', []);
|
|
|
|
if (empty($features)) {
|
|
break;
|
|
}
|
|
|
|
$records = collect($features)->map(fn (array $feature) => [
|
|
'town_pid' => $feature['attributes']['town_pid'],
|
|
'town_name' => $feature['attributes']['town_name'],
|
|
'state' => $feature['attributes']['state'],
|
|
'population' => $feature['attributes']['population'],
|
|
'town_class' => $feature['attributes']['town_class'],
|
|
'date_retired' => isset($feature['attributes']['date_retired'])
|
|
? Carbon::createFromTimestampMs($feature['attributes']['date_retired'])
|
|
: null,
|
|
'updated_at' => now(),
|
|
'created_at' => now(),
|
|
])->toArray();
|
|
|
|
Town::upsert($records, uniqueBy: ['town_pid'], update: [
|
|
'town_name',
|
|
'state',
|
|
'population',
|
|
'town_class',
|
|
'date_retired',
|
|
'updated_at',
|
|
]);
|
|
|
|
$synced += count($features);
|
|
$offset += self::PAGE_SIZE;
|
|
} while (count($features) === self::PAGE_SIZE);
|
|
|
|
Log::info("SyncTowns: synced {$synced} towns.");
|
|
}
|
|
}
|