Пошаговая очистка HL-справочника

Пошаговая очистка HL-справочника

1166
23.11.2015

Уже достаточно давно у битрикса появился новый тип свойств инфоблоков, "Справочник", основанный на привязке элементов инфоблока к элементам хайлоад-инфоблоков. Как известно, данный вид свойств имеет связку не по ИД значения справочника, а по полю "UF_XML_ID". Иногда может возникнуть ситуация, когда в справочнике дублируются значения с одинаковым внешним кодом (при не очень грамотно построенном механизме добавления новых значений в справочник). Тогда на помощь в очистке справочника может прити данный скрипт.

Уже достаточно давно у битрикса появился новый тип свойств инфоблоков, "Справочник", основанный на привязке элементов инфоблока к элементам хайлоад-инфоблоков. Как известно, данный вид свойств имеет связку не по ИД значения справочника, а по полю "UF_XML_ID". Иногда может возникнуть ситуация, когда в справочнике дублируются значения с одинаковым внешним кодом (при не очень грамотно построенном механизме добавления новых значений в справочник). Тогда на помощь в очистке справочника может прити данный скрипт.

Я для каждого проекта создаю отдельный модуль, в который помещаю все функции и константы проекта, поэтому в коде будут появляться некоторые методы абстрактного класса `siteclass` из модуля `sitemodule`.

Начнем с подключения модуля:

<? require($_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php');
    \Bitrix\Main\Loader::includeModule('sitemodule');

Далее - определяем сущность класса и выбираем ИД хайлоад-инфоблока со справочником:

$mainEntity = new siteclass();
    $entityID = $mainEntity::ATC;  // ATC - константа с ИД хайлоад-инфоблока

и выбираем все значения из справочника:

$tmp = $mainEntity->GetAllInVacabulary($entityID);
    $arRows = array();
    foreach ($tmp['ITEMS'] as $arRow) {
        $arRows[] = $arRow;
    }

Метод, выбирающий все значения из справочника имеет вид:

function GetAllInVacabulary($entity){
        return self::SearchInHLVacabulary('', $entity);
    }
    function SearchInHLVacabulary($code, $VHlblock, $groupKey = 'UF_XML_ID'){
        $code = trim($code);
        $cacheParams = array(
            'code' => $code,
            'entity_id' => $VHlblock,
            'module' => 'HighloadIblock',
            'function' => __FUNCTION__
        );
        $cache_id = md5(serialize($cacheParams));
        $cache_dir = __CLASS__ . '/' . $cacheParams['module'];
        $obCache = new CPHPCache;
        if ($obCache->InitCache(36000, $cache_id, $cache_dir)) {
            $arResult = $obCache->GetVars();
            $arResult['CACHE'] = 'Y';
        } elseif (\Bitrix\Main\Loader::includeModule('highloadblock') && $obCache->StartDataCache()) {
            global $CACHE_MANAGER;
            $CACHE_MANAGER->StartTagCache($cache_dir);
            $hlblock_requests = HL\HighloadBlockTable::getById($VHlblock)->fetch();//requests
            $entity_requests = HL\HighloadBlockTable::compileEntity($hlblock_requests);
            $entity_requests_data_class = $entity_requests->getDataClass();
            $main_query_requests = new Entity\Query($entity_requests_data_class);
            $main_query_requests->setSelect(array('ID', 'UF_NAME', 'UF_XML_ID'));
            if (strlen($code) > 0) {
                $main_query_requests->setFilter(array('UF_XML_ID' => $code));
            }
            $result_requests = $main_query_requests->exec();
            $result_requests = new CDBResult($result_requests);
            $arResult['ITEMS'] = array();
            while ($row_requests = $result_requests->Fetch()) {
                $arResult['ITEMS'][$row_requests[$groupKey]] = $row_requests;
                $CACHE_MANAGER->RegisterTag('hl' . $cacheParams['entity_id'] . $row_requests[$groupKey]);
            }
            $CACHE_MANAGER->RegisterTag('hl' . $cacheParams['entity_id']);
            $CACHE_MANAGER->EndTagCache();
            $obCache->EndDataCache($arResult);
        } else {
            $arResult['ITEMS'] = array();
        }
        return $arResult;
    }

Далее пишем пошаговый аяксовый js-обработчик/ Контейнер для информации о ходе обработки:

<div id="loading"><p class="persents">
    Обработка: <span class="pv">0</span>% (Строка: <span class="rv">1</span>/<?= count($arRows) ?>)
    </p>
    </div>

И сам js-скрипт (простите за смесь jquery и битриксовой JS-библиотеки - я ленивый и очень не хочется имеющийся в битриксе прелоадер переписывать):

<script type="text/javascript">
    var ajaxurl = '/bitrix/admin/sitemodule_ajax.php';  // url to pass ajax requests
    var Rows = <?=json_encode($arRows);?>;
    var countRows = <?=count($arRows);?>;
    function work_with_row(num, callback, triesCount) {
        var Persents = (parseInt(num) + 1) * 100 / parseInt(countRows);
        $('p.persents').find('span.pv').html(Math.round(Persents));
        $('p.persents').find('span.rv').html(parseInt(num) + 1);
        $.ajax({
            type: "POST",
            url: ajaxurl,
            data: {action: 'ClearVacabulary', Row: Rows[num],entity:<?=$entityID?>},
            dataType: "json",
            async: false,
            success: function () {
                callback(num + 1);
            },
            error: function () {
                triesCount = triesCount || 0;
                if (triesCount > 5) {
                    callback(num + 1);
                } else {
                    setTimeout(function () {
                        work_with_row(num, callback, triesCount + 1);
                    }, 5000);
                }
            }
        });
    }
    $(document).ready(function () {
        var wait = BX.showWait('loading');
        var maxIndex = Rows.length;
        var run = function (index) {
            if (index < maxIndex) {
                setTimeout(function () {
                    work_with_row(index, run);
                }, index > 0 ? 200 : 0)
            } else {
                $('p.persents').html('Обработка завершена');
                BX.closeWait('loading', wait);
            }
        };
        run(0);
    });
    </script>

немного по коду: Сначала определяем путь к файлу-обработчику аяксовых-запросов. Далее - определяем переменные с набором полученных значений справочника и с количеством этих значений. Функция work_with_row обрабатывает одно конкретное значение справочника. Подробнее о функции - ниже. Работа скрипта начинается с показа битриксового прелоадера и определения значения, когда обработчику пора остановиться. Дальше - вызываем функцию-обработчик шага, которая с таймаутом в 200 милисекунд запускает обработку выбранного шага с рекурсией. Когда все строки обработаны - показываем пользователю соответствующее сообщение.

Теперь по функции обработки каждой строки. Сначала функция меняет индикатор работы скрипта. После этого, идет отправка аякс-запроса на сервер. В случае успешно отработавшего запроса, запускается обработка следующего шага. Если же обработка аякс-запроса не отработала (сервер, например свалился, или интернет-соединение пропало), делаем еще 5 попыток с увеличенным таймаутом (5 сек.). Если за 5 попыток так и не удалось произвести запрос - пропускаем его.

Последний штрих - обработчик аякс-запроса. В принимающем файле прописываем:

\Bitrix\Main\Loader::includeModule('sitemodule');
    $mainEntity = new siteclass();
    $arRow = $_POST['Row'];
    $entityID = $_POST['entity'];
    if(!empty($arRow) && intval($arRow['ID'])>0 && intval($entityID)>0){
        $tmp=$mainEntity->SearchInHLVacabulary($arRow['UF_XML_ID'],$entityID,'ID');
        if(count($tmp['ITEMS'])>1){
            $isFirst = true;
            $arResult['ITEMS'] = $tmp['ITEMS'];
            foreach ($tmp['ITEMS'] as $arSubRow) {
                if($isFirst){
                    $isFirst = false;
                    continue;
                }
                $arResult['RES'] = $mainEntity->RemoveFromHlVacabulary(intval($arSubRow['ID']),$entityID);
            }
        }
    }
    echo json_encode($arResult);

Снова подключаем класс, определяем сущность, получаем инфомрацию о строке. Далее - ищем информацию в справочнике по значению XML_ID - результатом будет список всех значений справочника с одинаковым внешним кодом. Далее - запускаем обработку по полученным значениям (если значений больше 1). Первое значение пропускаем, а все остальные - удаляем. Метод SearchInHLVacabulary имеет вид:

function RemoveFromHlVacabulary($arValueID, $VHlblock){
    if (!\Bitrix\Main\Loader::includeModule('highloadblock')) return false;
    if(intval($arValueID)<=0) return false;
    global $CACHE_MANAGER;
    $rsData = HL\HighloadBlockTable::getList(array('filter' => array('ID' => $VHlblock)));
    if (!($arData = $rsData->fetch())) {
        return 'Инфоблок не найден';
    }
    $Entity = HL\HighloadBlockTable::compileEntity($arData);
    $DataClass = $Entity->getDataClass();
    $result = $DataClass::delete($arValueID);
    if (!$result->isSuccess())
        return array('error' => $result->getErrorMessages());
    else {
        $CACHE_MANAGER->ClearByTag('hl' . $VHlblock . $arValueID);
        $CACHE_MANAGER->ClearByTag('hl' . $VHlblock);
        return 'OK!';
    }
}

Вот такой вот маленький пример:) Понимаю, что для удаления можно было бы и более оптимальный код написать, с меньшим количеством отправок данных на сервер, но на примере этого кода можно делать не только удаление, но и более тяжелые обработки данных



Благодарю за внимание! Делитесь вашими замечаниями в комментариях ниже.


P.S. Обращайтесь ко мне за приобретением лицензий и продлений на 1C-Битрикс "Управление сайтом", лицензий на облачную и коробочную версии Битрикс 24 а также за приобретением и внедрением готовых решений на базе 1С-Битрикс от партнеров. За более подробной информацией свяжитесь со мной любым удобным для вас способом


Комментарии

Еще никто не комментировал данную публикацию. Будьте первыми!

Добавить комментарий

captcha

Возврат к списку