116 lines
3.8 KiB
PHP
116 lines
3.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Services\Countries;
|
|
|
|
use Illuminate\Http\Client\Factory as HttpFactory;
|
|
use RuntimeException;
|
|
|
|
final class CountryRemoteProvider implements CountryRemoteProviderInterface
|
|
{
|
|
public function __construct(
|
|
private readonly HttpFactory $http,
|
|
) {
|
|
}
|
|
|
|
public function fetchAll(): array
|
|
{
|
|
$endpoint = trim((string) config('skinbase-countries.endpoint', ''));
|
|
|
|
if ($endpoint === '') {
|
|
throw new RuntimeException('Country sync endpoint is not configured.');
|
|
}
|
|
|
|
$response = $this->http->acceptJson()
|
|
->connectTimeout(max(1, (int) config('skinbase-countries.connect_timeout', 5)))
|
|
->timeout(max(1, (int) config('skinbase-countries.timeout', 10)))
|
|
->retry(
|
|
max(0, (int) config('skinbase-countries.retry_times', 2)),
|
|
max(0, (int) config('skinbase-countries.retry_sleep_ms', 250)),
|
|
throw: false,
|
|
)
|
|
->get($endpoint);
|
|
|
|
if (! $response->successful()) {
|
|
throw new RuntimeException(sprintf('Country sync request failed with status %d.', $response->status()));
|
|
}
|
|
|
|
$payload = $response->json();
|
|
|
|
if (! is_array($payload)) {
|
|
throw new RuntimeException('Country sync response was not a JSON array.');
|
|
}
|
|
|
|
return $this->normalizePayload($payload);
|
|
}
|
|
|
|
public function normalizePayload(array $payload): array
|
|
{
|
|
$normalized = [];
|
|
|
|
foreach ($payload as $record) {
|
|
if (! is_array($record)) {
|
|
continue;
|
|
}
|
|
|
|
$country = $this->normalizeRecord($record);
|
|
|
|
if ($country !== null) {
|
|
$normalized[] = $country;
|
|
}
|
|
}
|
|
|
|
return $normalized;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, mixed> $record
|
|
* @return array<string, mixed>|null
|
|
*/
|
|
private function normalizeRecord(array $record): ?array
|
|
{
|
|
$iso2 = strtoupper(trim((string) ($record['cca2'] ?? $record['iso2'] ?? '')));
|
|
|
|
if (! preg_match('/^[A-Z]{2}$/', $iso2)) {
|
|
return null;
|
|
}
|
|
|
|
$iso3 = strtoupper(trim((string) ($record['cca3'] ?? $record['iso3'] ?? '')));
|
|
$iso3 = preg_match('/^[A-Z]{3}$/', $iso3) ? $iso3 : null;
|
|
|
|
$numericCode = trim((string) ($record['ccn3'] ?? $record['numeric_code'] ?? ''));
|
|
$numericCode = preg_match('/^\d{1,3}$/', $numericCode)
|
|
? str_pad($numericCode, 3, '0', STR_PAD_LEFT)
|
|
: null;
|
|
|
|
$name = $record['name'] ?? [];
|
|
$nameCommon = trim((string) ($name['common'] ?? $record['name_common'] ?? ''));
|
|
|
|
if ($nameCommon === '') {
|
|
return null;
|
|
}
|
|
|
|
$nameOfficial = trim((string) ($name['official'] ?? $record['name_official'] ?? ''));
|
|
$flags = $record['flags'] ?? [];
|
|
$flagSvgUrl = trim((string) ($flags['svg'] ?? $record['flag_svg_url'] ?? ''));
|
|
$flagPngUrl = trim((string) ($flags['png'] ?? $record['flag_png_url'] ?? ''));
|
|
$flagEmoji = trim((string) ($record['flag'] ?? $record['flag_emoji'] ?? ''));
|
|
$region = trim((string) ($record['region'] ?? ''));
|
|
$subregion = trim((string) ($record['subregion'] ?? ''));
|
|
|
|
return [
|
|
'iso2' => $iso2,
|
|
'iso3' => $iso3,
|
|
'numeric_code' => $numericCode,
|
|
'name_common' => $nameCommon,
|
|
'name_official' => $nameOfficial !== '' ? $nameOfficial : null,
|
|
'region' => $region !== '' ? $region : null,
|
|
'subregion' => $subregion !== '' ? $subregion : null,
|
|
'flag_svg_url' => $flagSvgUrl !== '' ? $flagSvgUrl : null,
|
|
'flag_png_url' => $flagPngUrl !== '' ? $flagPngUrl : null,
|
|
'flag_emoji' => $flagEmoji !== '' ? $flagEmoji : null,
|
|
];
|
|
}
|
|
}
|