*/ 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 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']; } }