Files
portal/app/app/Support/RussianRegions.php
T

211 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Support;
/**
* Канонический справочник субъектов РФ (1..89) — PHP-зеркало
* resources/js/constants/regions.ts (конституционный порядок, ст. 65).
* Sentinel 0 «Вся РФ» не входит (= NULL subject_code / пустой regions).
*
* ВАЖНО: при правке regions.ts синхронно править этот файл (тест RegionTagResolverTest
* «mirrors regions.ts — exactly 89» ловит расхождение по count, но не по именам —
* сверять имена вручную при изменениях).
*/
final class RussianRegions
{
/** @var array<int, string> code(1..89) => официальное имя субъекта */
public const CODE_TO_NAME = [
// 24 республики
1 => 'Республика Адыгея',
2 => 'Республика Алтай',
3 => 'Республика Башкортостан',
4 => 'Республика Бурятия',
5 => 'Республика Дагестан',
6 => 'Донецкая Народная Республика',
7 => 'Республика Ингушетия',
8 => 'Кабардино-Балкарская Республика',
9 => 'Республика Калмыкия',
10 => 'Карачаево-Черкесская Республика',
11 => 'Республика Карелия',
12 => 'Республика Коми',
13 => 'Республика Крым',
14 => 'Луганская Народная Республика',
15 => 'Республика Марий Эл',
16 => 'Республика Мордовия',
17 => 'Республика Саха (Якутия)',
18 => 'Республика Северная Осетия — Алания',
19 => 'Республика Татарстан',
20 => 'Республика Тыва',
21 => 'Удмуртская Республика',
22 => 'Республика Хакасия',
23 => 'Чеченская Республика',
24 => 'Чувашская Республика',
// 9 краёв
25 => 'Алтайский край',
26 => 'Забайкальский край',
27 => 'Камчатский край',
28 => 'Краснодарский край',
29 => 'Красноярский край',
30 => 'Пермский край',
31 => 'Приморский край',
32 => 'Ставропольский край',
33 => 'Хабаровский край',
// 48 областей
34 => 'Амурская область',
35 => 'Архангельская область',
36 => 'Астраханская область',
37 => 'Белгородская область',
38 => 'Брянская область',
39 => 'Владимирская область',
40 => 'Волгоградская область',
41 => 'Вологодская область',
42 => 'Воронежская область',
43 => 'Запорожская область',
44 => 'Ивановская область',
45 => 'Иркутская область',
46 => 'Калининградская область',
47 => 'Калужская область',
48 => 'Кемеровская область',
49 => 'Кировская область',
50 => 'Костромская область',
51 => 'Курганская область',
52 => 'Курская область',
53 => 'Ленинградская область',
54 => 'Липецкая область',
55 => 'Магаданская область',
56 => 'Московская область',
57 => 'Мурманская область',
58 => 'Нижегородская область',
59 => 'Новгородская область',
60 => 'Новосибирская область',
61 => 'Омская область',
62 => 'Оренбургская область',
63 => 'Орловская область',
64 => 'Пензенская область',
65 => 'Псковская область',
66 => 'Ростовская область',
67 => 'Рязанская область',
68 => 'Самарская область',
69 => 'Саратовская область',
70 => 'Сахалинская область',
71 => 'Свердловская область',
72 => 'Смоленская область',
73 => 'Тамбовская область',
74 => 'Тверская область',
75 => 'Томская область',
76 => 'Тульская область',
77 => 'Тюменская область',
78 => 'Ульяновская область',
79 => 'Херсонская область',
80 => 'Челябинская область',
81 => 'Ярославская область',
// 3 города федерального значения
82 => 'Москва',
83 => 'Санкт-Петербург',
84 => 'Севастополь',
// 1 автономная область
85 => 'Еврейская автономная область',
// 4 автономных округа
86 => 'Ненецкий автономный округ',
87 => 'Ханты-Мансийский автономный округ — Югра',
88 => 'Чукотский автономный округ',
89 => 'Ямало-Ненецкий автономный округ',
];
/**
* Алиасы нестандартных форм реестра Россвязи → каноничное имя субъекта.
* Города фед. значения приходят с префиксом «г. »; «Республика Удмуртская» —
* перевёрнутый порядок слов; «Кемеровская область - Кузбасс обл.» — спец-форма.
*
* @var array<string, string>
*/
private const REGION_ALIASES = [
'г. Москва' => 'Москва',
'Город Москва' => 'Москва',
'г. Санкт-Петербург' => 'Санкт-Петербург',
'г. Санкт - Петербург' => 'Санкт-Петербург',
'г. Севастополь' => 'Севастополь',
'Республика Саха /Якутия/' => 'Республика Саха (Якутия)',
'Чувашская Республика - Чувашия' => 'Чувашская Республика',
'Кемеровская область - Кузбасс обл.' => 'Кемеровская область',
'Кемеровская область - Кузбасс область' => 'Кемеровская область',
'Кемеровская область - Кузбасс' => 'Кемеровская область',
];
/** @return array<string, int> name => code (обратный индекс) */
public static function nameToCode(): array
{
return array_flip(self::CODE_TO_NAME);
}
/**
* Нормализует строку региона реестра Россвязи в каноничное имя субъекта (или null).
*
* Реестр кодирует субъект как ПОСЛЕДНИЙ сегмент после «|»
* (напр. «г. Воскресенск|р-н Воскресенский|Московская обл.» → «Московская обл.»),
* с сокращением «обл.» вместо «область» и рядом нестандартных форм (см. REGION_ALIASES).
* Безнадёжные/неоднозначные строки («-», «Российская Федерация»,
* «Москва и Московская область», «г.о. Тольятти») → null.
*/
public static function canonicalRegionName(string $raw): ?string
{
$segment = self::lastRegionSegment($raw);
if ($segment === '') {
return null;
}
// ХМАО приходит в множестве форм (em-dash/дефис, «Югра», « АО», капитализация) —
// ловим по двум устойчивым маркерам до общих правил.
if (mb_stripos($segment, 'Ханты') !== false && mb_stripos($segment, 'Мансийск') !== false) {
return 'Ханты-Мансийский автономный округ — Югра';
}
if (isset(self::REGION_ALIASES[$segment])) {
return self::REGION_ALIASES[$segment];
}
// «обл.» → «область»; « АО» → « автономный округ».
$name = (string) preg_replace('/\s*обл\.$/u', ' область', $segment);
$name = (string) preg_replace('/\s+АО$/u', ' автономный округ', $name);
// Дефис с пробелами → длинное тире (эталон: «Республика Северная Осетия — Алания»).
// Безопасно: ни одно каноническое имя не содержит дефис, окружённый пробелами
// (составные имена вроде «Кабардино-Балкарская» используют дефис без пробелов).
$name = str_replace(' - ', ' — ', $name);
if (isset(self::nameToCode()[$name])) {
return $name;
}
// Перевёрнутый порядок «Республика X» → «X Республика» (Удмуртская/Чеченская/
// Чувашская/Кабардино-Балкарская/Карачаево-Черкесская, Донецкая Народная/
// Луганская Народная). Республика-first каноны (Татарстан, Карелия…) уже
// отловлены прямым попаданием выше.
if (preg_match('/^Республика\s+(.+)$/u', $name, $m) === 1) {
$reordered = trim($m[1]).' Республика';
if (isset(self::nameToCode()[$reordered])) {
return $reordered;
}
}
return null;
}
/** Резолвит строку региона реестра Россвязи в subject_code (1..89) или null. */
public static function resolveSubjectCode(string $raw): ?int
{
$name = self::canonicalRegionName($raw);
return $name === null ? null : (self::nameToCode()[$name] ?? null);
}
/** Последний сегмент после «|» (субъект в формате Россвязи), trimmed. */
private static function lastRegionSegment(string $raw): string
{
$parts = explode('|', $raw);
return trim((string) end($parts));
}
}