Skip to content

Instantly share code, notes, and snippets.

@di7spider
Last active August 4, 2022 08:58
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save di7spider/1d183bbe1657538190a8 to your computer and use it in GitHub Desktop.
Save di7spider/1d183bbe1657538190a8 to your computer and use it in GitHub Desktop.
1C-Bitrix Loc2Fias :: Связка ФИАС (kladr-api.ru) с 1С-Битрикс местоположениями версии 2.0 (города/села) по ZIP коду
<?php
namespace Exchanges;
/**
* Связка ФИАС (kladr-api.ru) с 1С-Битрикс местоположениями версии 2.0 (города/села) по ZIP коду
*
* https://kladr-api.ru/
* https://dev.1c-bitrix.ru/learning/course/?COURSE_ID=43&LESSON_ID=3570
*/
class Loc2Fias
{
/** URL API */
const URL = 'http://kladr-api.ru/api.php';
/** Параметры запрса */
static protected $params = Array(
'token' => ''
);
/**
* Типы (/bitrix/admin/sale_location_type_list.php?lang=ru)
* BX => kladr-api.ru
*/
static protected $types = Array(
'CITY' => 'Город',
'VILLAGE' => 'Село'
);
static protected $extFiasId = null;
static protected $zip2Loc = Array();
static protected $status = Array();
static protected $codes = Array();
/** Импорт */
static public function import($params)
{
$result = Array();
$init = self::init();
if($init['id']){
if( is_array($params['query']) ){
self::$params = array_merge(self::$params, $params['query']);
}
$dataZip = self::data('ZIP');
if($init['status'] == 1){
$dataFias = self::data('FIAS');
$dataZip = array_diff_key($dataZip, $dataFias);
}
if( !empty($dataZip) ){
self::$extFiasId = $init['id'];
self::$zip2Loc = array_flip($dataZip);
self::query(
Array(
'zip' => array_values($dataZip),
'limit' => $params['limit']
)
);
}
}
return self::status('get');
}
/** Статус обработки */
static public function status($type, $data)
{
if($type == 'get'){
return self::$status;
}else{
self::$status[$type][] = $data;
}
}
/** Добавление новой записи в БД */
static public function add($zip, $code)
{
if($zip && $code){
if(self::$codes[$code]){
self::status('err', Array('err_double_code', $zip, $code) );
}else{
$add = Array(
'SERVICE_ID' => self::$extFiasId,
'XML_ID' => $code,
'LOCATION_ID' => self::$zip2Loc[$zip]
);
$res = \Bitrix\Sale\Location\ExternalTable::add($add);
if( $res-> isSuccess() ){
self::status('success', $add);
self::$codes[$code] = 1;
}else{
self::status('err', $add);
}
}
}else{
self::status('err', Array('err_zip_code', $zip, $code) );
}
}
/** Существует ли тип ФИАС в БД сайта, если нет то создаем */
static public function init()
{
$item = \Bitrix\Sale\Location\ExternalServiceTable::getList(
array(
'limit' => 1,
'filter' => Array(
'CODE' => 'FIAS'
),
'select' => Array(
'ID'
)
)
)-> fetch();
if( is_array($item) ){
return Array(
'id' => $item['ID'],
'status' => 1
);
}else{
$res = \Bitrix\Sale\Location\ExternalServiceTable::add(
array(
'CODE' => 'FIAS'
)
);
if( $res-> isSuccess() ){
return Array(
'id' => $res-> getId(),
'status' => 2
);
}else{
self::status('err', 'fias_type_add');
return Array(
'id' => null,
'status' => 3
);
}
}
}
/** Возвращает список loc = zip/fias */
static public function data($code)
{
if( empty($code) ){ return; }
$ext = \Bitrix\Sale\Location\ExternalTable::getList(
Array(
'filter' => Array(
'=SERVICE.CODE' => $code,
'=LOCATION.TYPE.CODE' => array_keys(self::$types)
),
'select' => Array(
'LOCATION_ID',
'XML_ID',
'SERVICE.CODE'
)
)
);
$result = $is = Array();
while( $item = $ext-> fetch() ){
$loc = (int) $item['LOCATION_ID'];
$xmlId = (string) $item['XML_ID'];
if( $loc && !empty($xmlId) && !$result[$loc] && !$is[$xmlId] ){
$result[$loc] = $xmlId;
$is[$xmlId] = 1;
}
}
return $result;
}
/** Анализатор ответа ФИАС (kladr-api.ru) */
static public function analyze($zip, $params)
{
$params = json_decode($params, true);
$params = $params['result'];
if( !empty($zip) && !empty($params) ){
$params = array_shift($params);
if( is_array($params['parents']) ){
foreach($params['parents'] as $item){
$id = preg_replace("/[^\d]+/", "", $item['id']);
if( !empty($id) && (
in_array($item['type'], self::$types) || $item['contentType'] == 'city' ) ){
self::add($zip, $id);
return $id;
}else{
$err = 'err_no_detect_city';
}
}
}else{
$err = 'err_no_parents';
}
}else{
$err = 'err_query_response';
}
$err && self::status(
'err',
Array(
$err,
$zip,
$params
)
);
}
/** Получаем родительские объекты по ZIP из ФИАС (kladr-api.ru) */
static public function query($params)
{
if( !empty($params['zip']) ){
$base = array_merge(
Array(
'contentType' => 'building',
'limit' => 1,
'withParent' => 1
),
self::$params
);
$listZip = array_chunk(
array_values($params['zip']),
isset($params['limit']) ? (int) $params['limit']: 50
);
$loc = array_flip($loc);
$mh = curl_multi_init();
foreach($listZip as $chunkZip){
foreach($chunkZip as $zip){
$query[$zip] = curl_init();
curl_setopt_array(
$query[$zip],
Array(
CURLOPT_URL => self::URL."?".http_build_query(
array_merge(
$base,
Array(
'zip' => $zip
)
)
),
CURLOPT_HEADER => 0,
CURLOPT_NOBODY => 0,
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_FOLLOWLOCATION => 1,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_MAXREDIRS => 3,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 20,
CURLOPT_SSL_VERIFYPEER => 0,
CURLOPT_SSL_VERIFYHOST => 0
)
);
curl_multi_add_handle($mh, $query[$zip]);
}
$active = null;
do{
$mrc = curl_multi_exec($mh, $active);
}while($mrc == CURLM_CALL_MULTI_PERFORM);
while($active && $mrc == CURLM_OK){
if (curl_multi_select($mh) != -1) {
do{
$mrc = curl_multi_exec($mh, $active);
}while($mrc == CURLM_CALL_MULTI_PERFORM);
if( $mhinfo = curl_multi_info_read($mh) ){
$zip = array_search($mhinfo['handle'], $query);
if($zip !== false){
self::analyze(
$zip,
curl_multi_getcontent($mhinfo['handle'])
);
}
curl_multi_remove_handle($mh, $mhinfo['handle']);
curl_close($mhinfo['handle']);
}
}
}
}
curl_multi_close($mh);
}
}
}
?>
@di7spider
Copy link
Author

Пример использования:

$result = \Exchanges\Loc2Fias::import(
  Array(
    'query' => Array(
       'token' => '' /** Токен от https://kladr-api.ru/ */
    ),
    'limit' => 50
  )
);

echo "<pre>";
  var_dump($result);
echo "</pre>";

@di7spider
Copy link
Author

Пример получившегося результата:
Пример результата №1
Пример результата №2
Пример результата №3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment