82 lines
3.0 KiB
PHP
82 lines
3.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use Illuminate\Validation\Rule;
|
|
use Illuminate\Validation\Rules\In;
|
|
|
|
/** Shared ISO identifiers; independent of administrative territories and IP data. */
|
|
final class CountryCatalogue
|
|
{
|
|
/** @return list<array{code: string, name: string, name_en: string}> */
|
|
public function all(): array
|
|
{
|
|
$countries = [];
|
|
$lines = file(resource_path('data/iso3166.tab'), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
|
|
if ($lines === false) {
|
|
throw new \RuntimeException('Catálogo ISO local indisponível.');
|
|
}
|
|
foreach ($lines as $line) {
|
|
if (str_starts_with($line, '#')) {
|
|
continue;
|
|
}
|
|
[$code, $english] = explode("\t", $line, 2);
|
|
$name = class_exists(\Locale::class) ? \Locale::getDisplayRegion('und_'.$code, 'pt') : $english;
|
|
$countries[] = ['code' => $code, 'name' => is_string($name) && $name !== '' ? $name : $english, 'name_en' => $english];
|
|
}
|
|
usort($countries, fn ($a, $b) => strnatcasecmp($a['name'], $b['name']));
|
|
|
|
return $countries;
|
|
}
|
|
|
|
public function rule(): In
|
|
{
|
|
return Rule::in(array_column($this->all(), 'code'));
|
|
}
|
|
|
|
/** Accept existing free-text integrations; validate ISO-shaped values without rewriting them. */
|
|
public function legacyCountryRule(): \Closure
|
|
{
|
|
$codes = array_column($this->all(), 'code');
|
|
|
|
return static function (string $attribute, mixed $value, \Closure $fail) use ($codes): void {
|
|
if (is_string($value) && preg_match('/^[a-zA-Z]{2}$/D', $value) && ! in_array($value, $codes, true)) {
|
|
$fail('Seleccione um código ISO de país válido.');
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Exact catalogue labels for reading legacy free-text country values.
|
|
* Does not infer request origin, alter stored values or classify unmatched text.
|
|
* Only labels belonging to one ISO country are included.
|
|
*
|
|
* @return list<string> Case-folded country code and catalogue labels.
|
|
*/
|
|
public function filterValues(string $code): array
|
|
{
|
|
$countries = $this->all();
|
|
$owners = [];
|
|
$selected = [];
|
|
foreach ($countries as $country) {
|
|
foreach (array_unique([$country['code'], $country['name'], $country['name_en']]) as $label) {
|
|
$key = mb_strtolower(trim($label), 'UTF-8');
|
|
$owners[$key][$country['code']] = true;
|
|
if ($country['code'] === $code) {
|
|
$selected[$key] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return array_values(array_filter(array_keys($selected), fn ($key) => count($owners[$key]) === 1));
|
|
}
|
|
|
|
/** @return array{code:string|null,name:string} */
|
|
public function describe(?string $code): array
|
|
{
|
|
$names = array_column($this->all(), 'name', 'code');
|
|
|
|
return isset($names[$code ?? '']) ? ['code' => $code, 'name' => $names[$code]] : ['code' => null, 'name' => 'Não informado'];
|
|
}
|
|
}
|