655 lines
28 KiB
PHP
Executable File
655 lines
28 KiB
PHP
Executable File
<?php
|
||
|
||
namespace backend\components;
|
||
|
||
use common\components\helpers\TableCreateHelper;
|
||
use common\components\IndependentQueryManager\IndependentQueryManager;
|
||
use common\components\ini\iniSet;
|
||
use common\models\DebuggingSoap;
|
||
use common\models\dictionary\Fias;
|
||
use common\models\dictionary\FiasDoma;
|
||
use common\models\dictionary\KladrCode;
|
||
use common\models\managers\BatchMaker;
|
||
use League\CLImate\CLImate;
|
||
use League\CLImate\TerminalObject\Dynamic\Progress;
|
||
use XBase\TableReader;
|
||
use Yii;
|
||
use yii\base\UserException;
|
||
use yii\helpers\ArrayHelper;
|
||
use yii\helpers\FileHelper;
|
||
|
||
class KladrLoader
|
||
{
|
||
|
||
public static function loadKladr(string $mode): array
|
||
{
|
||
if ($mode == 'file') {
|
||
return KladrLoader::loadKladrFromDBF();
|
||
}
|
||
if ($mode == 'university') {
|
||
if (KladrLoader::isOneSFiasAvailable()) {
|
||
return KladrLoader::loadKladrFromOneSFias();
|
||
}
|
||
throw new UserException("Сервисы для обновления адресного классификатора из Информационной системы вуза не доступны");
|
||
} else {
|
||
throw new UserException("Не удалось распознать способ обновления адресного классификатора");
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
public static function loadKladrFromDBF(?CLImate $climate = null, ?Progress $progress = null): array
|
||
{
|
||
iniSet::disableTimeLimit();
|
||
iniSet::extendMemoryLimit();
|
||
|
||
$iterate = 10000;
|
||
|
||
$files = [
|
||
'DOMA' => Yii::getAlias('@backend') . FileHelper::normalizePath('\web\conf\DOMA.dbf'),
|
||
'KLADR' => Yii::getAlias('@backend') . FileHelper::normalizePath('\web\conf\KLADR.dbf'),
|
||
'STREET' => Yii::getAlias('@backend') . FileHelper::normalizePath('\web\conf\STREET.dbf'),
|
||
];
|
||
|
||
$errors = [];
|
||
|
||
Yii::$app->db->createCommand()->truncateTable('dictionary_fias')->execute();
|
||
Yii::$app->db->createCommand()->truncateTable('dictionary_fias_doma')->execute();
|
||
Yii::$app->db->createCommand()->truncateTable('kladr_codes')->execute();
|
||
|
||
foreach ($files as $key => $file) {
|
||
if ($climate) {
|
||
$climate->darkGray()->out(Yii::t(
|
||
'console',
|
||
'Загрузка <bold>«<white>{FILE}</white>»</bold>',
|
||
['FILE' => $file]
|
||
));
|
||
}
|
||
|
||
if ($key == 'DOMA') {
|
||
$tables_creator = Yii::createObject(TableCreateHelper::class);
|
||
$tables_creator->createTempTable('fias_kladr_doma_union_temp', [
|
||
'kladr_code' => 'VARCHAR(255) NOT NULL',
|
||
'name' => 'VARCHAR(255) NOT NULL',
|
||
'kladr_index' => 'VARCHAR(255) NOT NULL',
|
||
]);
|
||
Yii::$app->db
|
||
->createCommand("CREATE INDEX temp_kladr_code ON fias_kladr_doma_union_temp (kladr_code);")
|
||
->execute();
|
||
|
||
$kladr_codes = [];
|
||
$table = new TableReader($file, ['encoding' => 'CP866', 'columns' => ['name', 'korp', 'code', 'index', 'gninmb', 'uno', 'ocatd']]);
|
||
$progressCount = 0;
|
||
if ($progress) {
|
||
$progressCount = $table->getRecordCount();
|
||
$progress->total($progressCount);
|
||
}
|
||
for ($I = 0; $row = $table->nextRecord(); $I += $iterate) {
|
||
if ($progress && $I % 10 == 0) {
|
||
$progress->current($I);
|
||
}
|
||
$kladr_codes[] = ['code' => $row->code];
|
||
$buffer = [];
|
||
$buffer[0] = [];
|
||
$buffer[0]['kladr_code'] = $row->code;
|
||
$buffer[0]['name'] = $row->name;
|
||
$buffer[0]['kladr_index'] = $row->index;
|
||
for ($j = 1; $j < $iterate && $row = $table->nextRecord(); $j++) {
|
||
$buffer[$j] = [];
|
||
try {
|
||
$kladr_codes[] = ['code' => $row->code];
|
||
$buffer[$j]['kladr_code'] = $row->code;
|
||
$buffer[$j]['name'] = $row->name;
|
||
$buffer[$j]['kladr_index'] = $row->index;
|
||
} catch (\Throwable $e) {
|
||
Yii::error("Ошибка смены кодировки $key.dbf: {$e->getMessage()} {$j}");
|
||
Yii::error(print_r($buffer, true));
|
||
throw $e;
|
||
}
|
||
|
||
unset($row);
|
||
}
|
||
|
||
try {
|
||
Yii::$app->db->createCommand()->batchInsert(KladrCode::tableName(), ['code'], $kladr_codes)->execute();
|
||
$kladr_codes = [];
|
||
Yii::$app->db->createCommand()->batchInsert('fias_kladr_doma_union_temp', ['kladr_code', 'name', 'kladr_index'], $buffer)->execute();
|
||
|
||
unset($buffer);
|
||
} catch (\Throwable $e) {
|
||
Yii::error("Ошибка установки $key.dbf: {$e->getMessage()}");
|
||
Yii::error(print_r($buffer, true));
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
if ($progress && $progressCount) {
|
||
$progress->current($progressCount);
|
||
}
|
||
$quoted_index_name = IndependentQueryManager::quoteEntity('index');
|
||
$quoted_name = IndependentQueryManager::quoteEntity('name');
|
||
$quoted_code_id = IndependentQueryManager::quoteEntity('code_id');
|
||
Yii::$app->db->createCommand("
|
||
INSERT INTO dictionary_fias_doma ({$quoted_code_id}, {$quoted_name}, {$quoted_index_name})
|
||
SELECT kladr_codes.id, fias_kladr_doma_union_temp.name, fias_kladr_doma_union_temp.kladr_index
|
||
FROM fias_kladr_doma_union_temp
|
||
LEFT JOIN kladr_codes ON kladr_codes.code = fias_kladr_doma_union_temp.kladr_code
|
||
")
|
||
->execute();
|
||
|
||
|
||
Yii::$app->db->createCommand()->dropTable('fias_kladr_doma_union_temp')->execute();
|
||
} else {
|
||
$table = new TableReader($file, ['encoding' => 'CP866', 'columns' => ['name', 'socr', 'code', 'index', 'gninmb', 'uno', 'ocatd']]);
|
||
$progressCount = 0;
|
||
if ($progress) {
|
||
$progressCount = $table->getRecordCount();
|
||
$progress->total($progressCount);
|
||
}
|
||
for ($I = 0; $row = $table->nextRecord(); $I += $iterate) {
|
||
if ($progress && $I % 10 == 0) {
|
||
$progress->current($I);
|
||
}
|
||
$buffer = [];
|
||
$code = $row->code;
|
||
$actualCode = (int)substr($code, -2);
|
||
$isActual = false;
|
||
if ($actualCode == 0) {
|
||
$buffer[0] = [];
|
||
$isActual = true;
|
||
}
|
||
if ($isActual) {
|
||
$buffer[0]['name'] = $row->name;
|
||
$buffer[0]['short'] = $row->socr;
|
||
$buffer[0]['code'] = $code;
|
||
$buffer[0]['zip_code'] = $row->index;
|
||
$kladr_array = static::parseKladrCode($code);
|
||
$buffer[0]['address_element_type'] = (string)static::getElementType($kladr_array);
|
||
$buffer[0]['area_code'] = (string)$kladr_array['area_code'];
|
||
$buffer[0]['city_code'] = (string)$kladr_array['city_code'];
|
||
$buffer[0]['region_code'] = (string)$kladr_array['region_code'];
|
||
$buffer[0]['street_code'] = (string)$kladr_array['street_code'];
|
||
$buffer[0]['village_code'] = (string)$kladr_array['village_code'];
|
||
}
|
||
for ($j = 1; $j < $iterate && $row = $table->nextRecord(); $j++) {
|
||
$code = $row->code;
|
||
$actualCode = (int)substr($code, -2);
|
||
$isActual = false;
|
||
if ($actualCode == 0) {
|
||
$buffer[$j] = [];
|
||
$isActual = true;
|
||
}
|
||
if ($isActual) {
|
||
$buffer[$j]['name'] = $row->name;
|
||
$buffer[$j]['short'] = $row->socr;
|
||
$buffer[$j]['code'] = $code;
|
||
$buffer[$j]['zip_code'] = $row->index;
|
||
$kladr_array = static::parseKladrCode($code);
|
||
$buffer[$j]['address_element_type'] = (string)static::getElementType($kladr_array);
|
||
$buffer[$j]['area_code'] = (string)$kladr_array['area_code'];
|
||
$buffer[$j]['city_code'] = (string)$kladr_array['city_code'];
|
||
$buffer[$j]['region_code'] = (string)$kladr_array['region_code'];
|
||
$buffer[$j]['street_code'] = (string)$kladr_array['street_code'];
|
||
$buffer[$j]['village_code'] = (string)$kladr_array['village_code'];
|
||
}
|
||
|
||
unset($row);
|
||
}
|
||
|
||
try {
|
||
Yii::$app->db->createCommand()->batchInsert(
|
||
'dictionary_fias',
|
||
[
|
||
'name',
|
||
'short',
|
||
'code',
|
||
'zip_code',
|
||
'address_element_type',
|
||
'area_code',
|
||
'city_code',
|
||
'region_code',
|
||
'street_code',
|
||
'village_code'
|
||
],
|
||
$buffer
|
||
)->execute();
|
||
|
||
unset($buffer);
|
||
} catch (\Throwable $e) {
|
||
Yii::error("Ошибка установки (номер группы $I) $key.dbf: {$e->getMessage()}");
|
||
Yii::error(print_r($buffer, true));
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
if ($progress) {
|
||
$progress->current($progressCount);
|
||
}
|
||
}
|
||
|
||
if ($climate) {
|
||
$climate->green()->out(Yii::t(
|
||
'console',
|
||
'Загрузка <bold>«<white>{FILE}</white>»</bold> завершена успешно',
|
||
['FILE' => $file]
|
||
));
|
||
}
|
||
}
|
||
|
||
return $errors;
|
||
}
|
||
|
||
public static function loadKladrFromOneSFias(): array
|
||
{
|
||
iniSet::disableTimeLimit();
|
||
iniSet::extendMemoryLimit();
|
||
try {
|
||
foreach (KladrLoader::fetchRegionList() as $number => $name) {
|
||
KladrLoader::loadRegion($number);
|
||
}
|
||
return [];
|
||
} catch (\Throwable $e) {
|
||
Yii::error($e->getMessage(), 'loadKladr');
|
||
return [$e->getMessage()];
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
public static function parseKLADRCode(string $code): array
|
||
{
|
||
$region_code = substr($code, 0, 2);
|
||
$area_code = substr($code, 2, 3);
|
||
$city_code = substr($code, 5, 3);
|
||
$village_code = substr($code, 8, 3);
|
||
$street_code = substr($code, 11, 4);
|
||
|
||
|
||
$region_code = ltrim((string)$region_code, '0') ?: '0';
|
||
$area_code = ltrim((string)$area_code, '0') ?: '0';
|
||
$city_code = ltrim((string)$city_code, '0') ?: '0';
|
||
$village_code = ltrim((string)$village_code, '0') ?: '0';
|
||
$street_code = ltrim((string)$street_code, '0') ?: '0';
|
||
|
||
return [
|
||
'region_code' => (int)$region_code,
|
||
'area_code' => (int)$area_code,
|
||
'city_code' => (int)$city_code,
|
||
'village_code' => (int)$village_code,
|
||
'street_code' => (int)$street_code,
|
||
];
|
||
}
|
||
|
||
protected static function getElementType(array $kladr_array): int
|
||
{
|
||
if ($kladr_array['street_code'] != 0) {
|
||
return 5;
|
||
} elseif ($kladr_array['village_code'] != 0) {
|
||
return 4;
|
||
} elseif ($kladr_array['city_code'] != 0) {
|
||
return 3;
|
||
} elseif ($kladr_array['area_code'] != 0) {
|
||
return 2;
|
||
} elseif ($kladr_array['region_code'] != 0) {
|
||
return 1;
|
||
}
|
||
throw new \UnexpectedValueException('Неизвестный тип элемента адреса');
|
||
}
|
||
|
||
public static function fetchRegionList(): array
|
||
{
|
||
$result = Yii::$app->soapClientAbit->load('GetFiasRegionsList', [], DebuggingSoap::getInstance()->isLoggingForKladrSoapEnabled);
|
||
if ($result && $result->return) {
|
||
$regions = json_decode((string)$result->return, false);
|
||
if ($regions) {
|
||
if (!is_array($regions) || ArrayHelper::isAssociative($regions)) {
|
||
$regions = [$regions];
|
||
}
|
||
return ArrayHelper::map($regions, 'Number', 'Name');
|
||
}
|
||
}
|
||
return [];
|
||
}
|
||
|
||
public static function isOneSFiasAvailable(): bool
|
||
{
|
||
try {
|
||
$result = \Yii::$app->dictionaryManager->GetInterfaceVersion('GetFiasRegionsList');
|
||
return version_compare($result, '0.0.18.12') >= 0;
|
||
} catch (\Throwable $e) {
|
||
\Yii::error("Не удалось получить версию метода GetFiasRegionsList: {$e->getMessage()}");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
private static function fetchRegionElements(string $region): \Generator
|
||
{
|
||
$start_uid = null;
|
||
do {
|
||
$result = Yii::$app->soapClientAbit->load('GetFiasRegionElements', [
|
||
'RegionNumber' => $region,
|
||
'FetchingItemsCount' => getenv("FIAS_FETCHING_ITEMS_COUNT") ? (int)getenv("FIAS_FETCHING_ITEMS_COUNT") : 5000,
|
||
'StartFiasIdentity' => $start_uid,
|
||
], DebuggingSoap::getInstance()->isLoggingForKladrSoapEnabled);
|
||
$start_uid = null;
|
||
if ($result && $result->return) {
|
||
$elements = json_decode((string)$result->return, false);
|
||
if ($elements) {
|
||
if (!is_array($elements) || ArrayHelper::isAssociative($elements)) {
|
||
$elements = [$elements];
|
||
}
|
||
foreach ($elements as $element) {
|
||
$start_uid = $element->FiasIdentity;
|
||
$clear_kladr_code = $element->KLADRCode;
|
||
if (!$clear_kladr_code) {
|
||
continue;
|
||
}
|
||
if (strlen((string)$clear_kladr_code) > 13) {
|
||
$clear_kladr_code = str_pad($clear_kladr_code, 17, '0', STR_PAD_LEFT);
|
||
} else {
|
||
$clear_kladr_code = str_pad($clear_kladr_code, 13, '0', STR_PAD_LEFT);
|
||
}
|
||
$full_region = str_pad($region, 2, '0', STR_PAD_LEFT);
|
||
$region_code = mb_substr($clear_kladr_code, 0, 2);
|
||
|
||
|
||
if ($region_code !== $full_region) {
|
||
if ($clear_kladr_code[-1] === '0') {
|
||
|
||
$clear_kladr_code = '0' . mb_substr($clear_kladr_code, 0, -1);
|
||
} else {
|
||
continue;
|
||
}
|
||
}
|
||
yield [
|
||
'fias_id' => $element->FiasIdentity,
|
||
'parent_fias_id' => $element->ParentFiasIdentity,
|
||
'name' => $element->Name,
|
||
'short' => $element->Short,
|
||
'code' => $clear_kladr_code,
|
||
'buildings' => $element->Buildings,
|
||
];
|
||
}
|
||
}
|
||
}
|
||
} while ($start_uid);
|
||
}
|
||
|
||
private static function purgeRegionItems(string $region): void
|
||
{
|
||
$trimmed_region = ltrim($region, '0') ?: '0';
|
||
if (Yii::$app->db->driverName === 'pgsql') {
|
||
Yii::$app->db
|
||
->createCommand("
|
||
DELETE FROM dictionary_fias_doma
|
||
USING dictionary_fias
|
||
WHERE dictionary_fias_doma.fias_id = dictionary_fias.fias_id
|
||
AND dictionary_fias.region_code = :trimmed_region
|
||
AND dictionary_fias_doma.fias_id IS NOT NULL
|
||
", compact('trimmed_region'))
|
||
->execute();
|
||
} else {
|
||
Yii::$app->db
|
||
->createCommand("
|
||
DELETE dictionary_fias_doma FROM dictionary_fias_doma
|
||
INNER JOIN dictionary_fias
|
||
ON dictionary_fias_doma.fias_id = dictionary_fias.fias_id AND dictionary_fias.region_code = :trimmed_region
|
||
WHERE dictionary_fias_doma.fias_id IS NOT NULL
|
||
", compact('trimmed_region'))
|
||
->execute();
|
||
}
|
||
|
||
$deleteQuery = "
|
||
DELETE FROM [[dictionary_fias_doma]]
|
||
WHERE fias_id IS NULL
|
||
LIMIT 100000
|
||
";
|
||
if (Yii::$app->db->driverName === 'pgsql') {
|
||
$deleteQuery = "
|
||
DELETE FROM [[dictionary_fias_doma]]
|
||
WHERE ctid IN (
|
||
SELECT ctid
|
||
FROM [[dictionary_fias_doma]]
|
||
WHERE fias_id IS NULL
|
||
LIMIT 100000
|
||
)
|
||
";
|
||
}
|
||
|
||
do {
|
||
$affectedRows = Yii::$app->db
|
||
->createCommand($deleteQuery)
|
||
->execute();
|
||
} while ($affectedRows > 0);
|
||
|
||
$deleteQuery = "
|
||
DELETE FROM [[dictionary_fias]]
|
||
WHERE region_code = :trimmed_region OR fias_id IS NULL
|
||
LIMIT 100000
|
||
";
|
||
if (Yii::$app->db->driverName === 'pgsql') {
|
||
$deleteQuery = "
|
||
DELETE FROM [[dictionary_fias]]
|
||
WHERE ctid IN (
|
||
SELECT ctid
|
||
FROM [[dictionary_fias]]
|
||
WHERE region_code = :trimmed_region OR fias_id IS NULL
|
||
LIMIT 100000
|
||
)
|
||
";
|
||
}
|
||
do {
|
||
$affectedRows = Yii::$app->db
|
||
->createCommand($deleteQuery, ['trimmed_region' => $trimmed_region])
|
||
->execute();
|
||
} while ($affectedRows > 0);
|
||
}
|
||
|
||
private static function getFiasTypesMap(): array
|
||
{
|
||
static $map;
|
||
if (!$map) {
|
||
$map = Yii::$app->cache->getOrSet('fias_types_map', function () {
|
||
try {
|
||
$result = Yii::$app->soapClientAbit->load_with_caching("GetFiasOwnershipsBuildingsTypes");
|
||
if (!empty($result->return)) {
|
||
$raw_map = json_decode((string)$result->return, false);
|
||
$result = [];
|
||
if (isset($raw_map->Ownerships)) {
|
||
$ownerships = [];
|
||
if (!is_array($raw_map->Ownerships)) {
|
||
$raw_map->Ownerships = [$raw_map->Ownerships];
|
||
}
|
||
foreach ($raw_map->Ownerships as $ownership) {
|
||
$ownerships[$ownership->Key] = $ownership->Value;
|
||
}
|
||
$result['ownerships'] = $ownerships;
|
||
}
|
||
if (isset($raw_map->Buildings)) {
|
||
$buildings = [];
|
||
if (!is_array($raw_map->Buildings)) {
|
||
$raw_map->Buildings = [$raw_map->Buildings];
|
||
}
|
||
foreach ($raw_map->Buildings as $building) {
|
||
$buildings[$building->Key] = $building->Value;
|
||
}
|
||
$result['buildings'] = $buildings;
|
||
}
|
||
return $result;
|
||
}
|
||
return [];
|
||
} catch (\Throwable $e) {
|
||
Yii::error("Не удалось получить карту типов ФИАС: {$e->getMessage()}");
|
||
return [];
|
||
}
|
||
}, 3600);
|
||
}
|
||
return $map;
|
||
}
|
||
|
||
private static function getOwnershipType(string $encoded): string
|
||
{
|
||
$map = KladrLoader::getFiasTypesMap();
|
||
if (isset($map['ownerships'][$encoded])) {
|
||
|
||
return mb_strtolower(mb_substr($map['ownerships'][$encoded], 0, 1));
|
||
}
|
||
return $encoded;
|
||
}
|
||
|
||
private static function getBuildingType(string $encoded): string
|
||
{
|
||
$map = KladrLoader::getFiasTypesMap();
|
||
if (isset($map['buildings'][$encoded])) {
|
||
return mb_strtolower(mb_substr($map['buildings'][$encoded], 0, 1));
|
||
}
|
||
return $encoded;
|
||
}
|
||
|
||
private static function decodeFiasId(string $fias_id): string
|
||
{
|
||
if (empty($fias_id)) {
|
||
return '';
|
||
}
|
||
$decoded = bin2hex(base64_decode($fias_id));
|
||
return mb_strcut($decoded, 0, 8) . '-' . mb_strcut($decoded, 8, 4) . '-' . mb_strcut($decoded, 12, 4) . '-' . mb_strcut($decoded, 16, 4) . '-' . mb_strcut($decoded, 20);
|
||
}
|
||
|
||
private static function parseBuilding(string $building): array
|
||
{
|
||
$result = [];
|
||
[$encoded_fias_id, $type, $name, $housing, $structure_type, $structure] = KladrLoader::parseBuildingString($building);
|
||
$type = KladrLoader::getOwnershipType((string)$type);
|
||
$structure_type = KladrLoader::getBuildingType((string)$structure_type);
|
||
$fias_id = KladrLoader::decodeFiasId($encoded_fias_id);
|
||
$result['fias_id'] = $fias_id;
|
||
$result['name'] = "";
|
||
if ($name) {
|
||
$result['name'] = "{$type} {$name}";
|
||
}
|
||
if ($housing) {
|
||
$result['name'] .= "/{$housing}";
|
||
}
|
||
if ($structure) {
|
||
$result['name'] .= "/{$structure_type} {$structure}";
|
||
}
|
||
$result['name'] = trim($result['name'], '/');
|
||
return $result;
|
||
}
|
||
|
||
public static function loadRegion(string $region, ?Progress $progress = null): void
|
||
{
|
||
iniSet::disableTimeLimit();
|
||
iniSet::extendMemoryLimit();
|
||
gc_disable();
|
||
$buildingsBatcher = new BatchMaker(5000, function (array $batch) {
|
||
if ($batch) {
|
||
FiasDoma::getDb()->createCommand()->batchInsert(FiasDoma::tableName(), array_keys($batch[0]), $batch)->execute();
|
||
}
|
||
});
|
||
$itemsBatcher = new BatchMaker(20000, function (array $batch) {
|
||
if ($batch) {
|
||
Fias::getDb()->createCommand()->batchInsert(Fias::tableName(), array_keys($batch[0]), $batch)->execute();
|
||
}
|
||
});
|
||
$transaction = Fias::getDb()->beginTransaction();
|
||
try {
|
||
KladrLoader::purgeRegionItems($region);
|
||
$regionElements = KladrLoader::fetchRegionElements($region);
|
||
$progressCount = 0;
|
||
if ($progress) {
|
||
$regionElements = iterator_to_array($regionElements);
|
||
$progressCount = count($regionElements);
|
||
$progress->total($progressCount);
|
||
}
|
||
$time = time();
|
||
foreach ($regionElements as $I => $item) {
|
||
if ($progress && $I % 3 == 0) {
|
||
$progress->current($I);
|
||
}
|
||
|
||
$item_info = array_merge($item, KladrLoader::parseKLADRCode($item['code']));
|
||
$item_info['address_element_type'] = KladrLoader::getElementType($item_info);
|
||
unset($item_info['buildings']);
|
||
$item_info['created_at'] = $time;
|
||
$item_info['updated_at'] = $time;
|
||
$itemsBatcher->add($item_info);
|
||
if ($item['buildings']) {
|
||
if (!is_array($item['buildings'])) {
|
||
$item['buildings'] = [$item['buildings']];
|
||
}
|
||
foreach ($item['buildings'] as $building_info) {
|
||
$clear_zip_code = $building_info->PostalIndex;
|
||
$buildings = explode("\t", (string)$building_info->BuildingsString);
|
||
foreach (array_chunk($buildings, 50) as $buildings_chunk) {
|
||
$names = array_reduce($buildings_chunk, function ($carry, $raw_building) {
|
||
$building = KladrLoader::parseBuilding($raw_building);
|
||
if (!empty($building['fias_id'])) {
|
||
$carry .= "{$building['name']}, ";
|
||
}
|
||
return $carry;
|
||
}, '');
|
||
$names = trim($names, ', ');
|
||
$building_info = [
|
||
'fias_id' => $item['fias_id'],
|
||
'name' => $names,
|
||
'index' => $clear_zip_code,
|
||
'created_at' => $time,
|
||
'updated_at' => $time,
|
||
];
|
||
$buildingsBatcher->add($building_info);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
$itemsBatcher->flush();
|
||
$buildingsBatcher->flush();
|
||
|
||
$transaction->commit();
|
||
|
||
if ($progress && $progressCount) {
|
||
$progress->current($progressCount);
|
||
}
|
||
} catch (\Throwable $e) {
|
||
$transaction->rollBack();
|
||
\Yii::error("Ошибка при загрузке ФИАС: " . $e->getMessage(), 'loadRegion');
|
||
throw $e;
|
||
}
|
||
}
|
||
|
||
private static function parseBuildingString(string $building): array
|
||
{
|
||
|
||
$equals_index = mb_strpos($building, '==');
|
||
if ($equals_index === false) {
|
||
return ['', '', '', '', '', ''];
|
||
}
|
||
|
||
$encoded_fias_id = mb_substr($building, 0, $equals_index + 2);
|
||
$tilde_index = mb_strpos($building, '~', $equals_index);
|
||
$type = mb_substr($building, $equals_index + 2, 1);
|
||
$name = mb_substr($building, $equals_index + 3, $tilde_index ? $tilde_index - $equals_index - 3 : null);
|
||
$housing = null;
|
||
$structure_type = null;
|
||
$structure = null;
|
||
|
||
if ($tilde_index) {
|
||
$second_tilde_index = mb_strpos($building, '~', $tilde_index + 1);
|
||
$housing = mb_substr($building, $tilde_index + 1, $second_tilde_index ? $second_tilde_index - $tilde_index - 1 : null);
|
||
if ($second_tilde_index) {
|
||
$third_tilde_index = mb_strpos($building, '~', $second_tilde_index + 1);
|
||
|
||
if ($third_tilde_index) {
|
||
$structure_type = mb_substr($building, $second_tilde_index + 1, $third_tilde_index - $second_tilde_index - 1);
|
||
$structure = mb_substr($building, $third_tilde_index + 1);
|
||
}
|
||
}
|
||
}
|
||
return [$encoded_fias_id, $type, $name, $housing, $structure_type, $structure];
|
||
}
|
||
}
|