/
scoder
/
tplink_parser
Обзор
Документация
Войти
/
scoder
/
tplink_parser
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
lib/iblockcreator.php
334 строки
13 KB
scoder
Загрузка изначальной версии
16 июл 2026, 14:19
16 июл 2026, 14:19
b6cbe01
Код
Авторство
О чём код?
<?php namespace Tplink\Parser; use Bitrix\Main\SiteTable; use Bitrix\Iblock\TypeTable; use Bitrix\Iblock\IblockTable; class IblockCreator { const IBLOCK_TYPE_ID = 'test_catalog'; const IBLOCK_CODE = 'tplink_catalog_stage'; const IBLOCK_NAME = 'TP-Link Catalog (Test Import)'; /** * Основной метод запуска создания структуры * @return int ID созданного или существующего инфоблока */ public static function installStructure(): int { if (!\Bitrix\Main\Loader::includeModule('iblock')) { return 0; } self::createIblockType(); $iblockId = self::createIblock(); if ($iblockId > 0) { self::createProperties($iblockId); } return $iblockId; } /** * Создание типа инфоблока */ private static function createIblockType(): void { $typeExists = TypeTable::getById(self::IBLOCK_TYPE_ID)->fetch(); if (!$typeExists) { $result = TypeTable::add([ 'ID' => self::IBLOCK_TYPE_ID, 'SECTIONS' => 'Y', 'IN_RSS' => 'N', 'SORT' => 9999999, ]); if ($result->isSuccess()) { $ibType = new \CIBlockType(); $ibType->Update(self::IBLOCK_TYPE_ID, [ 'LANG' => [ 'ru' => ['NAME' => 'Тестовые каталоги', 'SECTION_NAME' => 'Разделы', 'ELEMENT_NAME' => 'Элементы'], 'en' => ['NAME' => 'Test Catalogs', 'SECTION_NAME' => 'Sections', 'ELEMENT_NAME' => 'Elements'], ] ]); } } } /** * Создание инфоблока (ПЕРВАЯ ВЕРСИЯ СВОЙСТВ) */ private static function createIblock(): int { $iblock = IblockTable::getList([ 'filter' => [ '=IBLOCK_TYPE_ID' => self::IBLOCK_TYPE_ID, '=CODE' => self::IBLOCK_CODE ] ])->fetch(); if ($iblock) { if ($iblock['VERSION'] == 2) { \CIBlock::Delete((int)$iblock['ID']); } else { return (int)$iblock['ID']; } } $siteIds = []; $sitesRes = SiteTable::getList(['select' => ['LID']]); while ($site = $sitesRes->fetch()) { $siteIds[] = $site['LID']; } $ib = new \CIBlock(); $iblockId = (int)$ib->Add([ 'ACTIVE' => 'Y', 'NAME' => self::IBLOCK_NAME, 'CODE' => self::IBLOCK_CODE, 'IBLOCK_TYPE_ID' => self::IBLOCK_TYPE_ID, 'SORT' => 9999999, 'VERSION' => 1, 'SITE_ID' => $siteIds, 'GROUP_ID' => ['2' => 'R'] ]); return $iblockId; } /** * Создание и редактирование свойств с принудительным выведением ошибок */ private static function createProperties(int $iblockId): void { $properties = [ ['CODE' => 'FULL_ARTICLE', 'NAME' => 'Полный артикул', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'Y'], ['CODE' => 'ARTICLE', 'NAME' => 'Артикул', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'Y'], ['CODE' => 'CATEGORY', 'NAME' => 'Категория (текст из хлебных крошек TP-Link)', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'Y'], ['CODE' => 'WIFI_STANDARD', 'NAME' => 'Wi-Fi стандарт', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'N'], ['CODE' => 'WIFI_SPEED', 'NAME' => 'Скорость Wi-Fi', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'N'], ['CODE' => 'WAN_SPEED', 'NAME' => 'Скорость WAN-порта', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'N'], ['CODE' => 'LAN_PORTS', 'NAME' => 'Кол-во LAN-портов', 'PROPERTY_TYPE' => 'N', 'IS_REQUIRED' => 'N'], ['CODE' => 'SOURCE_URL', 'NAME' => 'Ссылка на карточку модели на источнике', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'Y'], ['CODE' => 'SYNCED_AT', 'NAME' => 'Дата и время последней синхронизации (ISO 8601)', 'PROPERTY_TYPE' => 'S', 'IS_REQUIRED' => 'Y'], [ 'CODE' => 'MISSING_AT_SOURCE', 'NAME' => 'Признак «на источнике не найден при последнем запуске»', 'PROPERTY_TYPE' => 'L', 'LIST_TYPE' => 'C', 'IS_REQUIRED' => 'Y', 'VALUES' => [ ['VALUE' => 'Y', 'XML_ID' => 'Y', 'DEF' => 'N', 'SORT' => 100], ['VALUE' => 'N', 'XML_ID' => 'N', 'DEF' => 'Y', 'SORT' => 200] ] ] ]; $ibp = new \CIBlockProperty(); $sortCounter = 10; foreach ($properties as $propFields) { $propFields['IBLOCK_ID'] = $iblockId; $propFields['SORT'] = $sortCounter; $sortCounter += 10; $enumValues = $propFields['VALUES'] ?? null; unset($propFields['VALUES']); // Отладка: смотрим, какое свойство сейчас обрабатывается echo "Обработка свойства: " . $iblockId . " " . $propFields['CODE'] . "... \n"; $propRes = \CIBlockProperty::GetList([], ['IBLOCK_ID' => $iblockId, 'CODE' => $propFields['CODE']]); if ($currentProp = $propRes->Fetch()) { $propId = (int)$currentProp['ID']; $updateFields = []; foreach (['NAME', 'PROPERTY_TYPE', 'IS_REQUIRED', 'LIST_TYPE', 'SORT'] as $field) { if (isset($propFields[$field]) && $propFields[$field] != $currentProp[$field]) { $updateFields[$field] = $propFields[$field]; } } if ($propFields['PROPERTY_TYPE'] === 'L' && !empty($enumValues)) { self::updatePropertyEnumValues($propId, $enumValues); } if (!empty($updateFields)) { $res = $ibp->Update($propId, $updateFields); if (!$res) { echo "<strong style='color:red;'>Ошибка ОБНОВЛЕНИЯ свойства {$propFields['CODE']}: " . $ibp->LAST_ERROR . "</strong><br>"; } else { echo "Свойство {$propFields['CODE']} успешно обновлено.<br>"; } } else { echo "Свойство {$propFields['CODE']} уже существует, изменений нет.<br>"; } } else { if ($propFields['PROPERTY_TYPE'] === 'L' && !empty($enumValues)) { $propFields['VALUES'] = $enumValues; } $propId = $ibp->Add($propFields); if (!$propId) { echo "<strong style='color:red;'>Ошибка ДОБАВЛЕНИЯ свойства {$propFields['CODE']}: " . $ibp->LAST_ERROR . "</strong><br>"; } else { echo "<span style='color:green;'>Свойство {$propFields['CODE']} успешно создано (ID: {$propId}).</span><br>"; } } echo "<hr>"; } } /** * Обновление вариантов значений списков */ private static function updatePropertyEnumValues(int $propId, array $enumValues): void { $ibpe = new \CIBlockPropertyEnum(); $existingEnums = []; $enumRes = \CIBlockPropertyEnum::GetList([], ['PROPERTY_ID' => $propId]); while ($enum = $enumRes->Fetch()) { $existingEnums[$enum['XML_ID']] = $enum; } foreach ($enumValues as $val) { $xmlId = $val['XML_ID']; if (isset($existingEnums[$xmlId])) { $enumId = (int)$existingEnums[$xmlId]['ID']; $enumUpdateFields = []; foreach (['VALUE', 'DEF', 'SORT'] as $enumField) { if (isset($val[$enumField]) && $val[$enumField] != $existingEnums[$xmlId][$enumField]) { $enumUpdateFields[$enumField] = $val[$enumField]; } } if (!empty($enumUpdateFields)) { $ibpe->Update($enumId, $enumUpdateFields); } } else { $val['PROPERTY_ID'] = $propId; $ibpe->Add($val); } } } /** * Создает или обновляет элемент инфоблока по его XML_ID (Классический API) * * @param int $iblockId ID инфоблока * @param string $xmlId Уникальный внешний код элемента (например, артикул) * @param string $name Название элемента * @param array $properties Массив свойств вида ['CODE' => 'VALUE'] * @param array $additionalFields Дополнительные базовые поля (например, ['PREVIEW_TEXT' => '...']) * @return int ID созданного или обновленного элемента */ public static function saveElement(int $iblockId, string $xmlId, string $name, array $properties = [], array $additionalFields = []): int { if ($iblockId <= 0 || empty($xmlId)) { return 0; } if (!\Bitrix\Main\Loader::includeModule('iblock')) { return 0; } $el = new \CIBlockElement(); // 1. Приведение XML_ID свойства-списка к внутреннему ID Битрикса if (isset($properties['MISSING_AT_SOURCE'])) { $enumRes = \CIBlockPropertyEnum::GetList( [], ['IBLOCK_ID' => $iblockId, 'CODE' => 'MISSING_AT_SOURCE', 'XML_ID' => $properties['MISSING_AT_SOURCE']] ); if ($enumFields = $enumRes->Fetch()) { $properties['MISSING_AT_SOURCE'] = $enumFields['ID']; } } // 2. Поиск существующего элемента по XML_ID $existingElement = \CIBlockElement::GetList( [], ['IBLOCK_ID' => $iblockId, '=XML_ID' => $xmlId, 'CHECK_PERMISSIONS' => 'N'], false, ['nTopCount' => 1], ['ID', 'NAME', 'PREVIEW_TEXT', 'ACTIVE'] )->Fetch(); // 3. Формирование базовых полей элемента $baseFields = array_merge([ 'NAME' => $name, 'ACTIVE' => $additionalFields['ACTIVE'] ?? 'Y', ], $additionalFields); if ($existingElement) { $elementId = (int)$existingElement['ID']; $updateFields = []; // Сверяем базовые поля перед обновлением, чтобы избежать лишней нагрузки foreach ($baseFields as $field => $value) { if (isset($existingElement[$field]) && $existingElement[$field] != $value) { $updateFields[$field] = $value; } } if (!empty($updateFields)) { $el->Update($elementId, $updateFields); } // Обновление свойств (для 1-й версии инфоблока) if (!empty($properties)) { \CIBlockElement::SetPropertyValuesEx($elementId, $iblockId, $properties); } } else { // Создание полностью нового элемента со свойствами $allFields = array_merge($baseFields, [ 'IBLOCK_ID' => $iblockId, 'XML_ID' => $xmlId, 'PROPERTY_VALUES' => $properties ]); $elementId = (int)$el->Add($allFields); if ($elementId <= 0) { return 0; } } return $elementId; } public static function resetElements(int $iblockId, array $xmls): void { if (!empty($xmls) && $iblockId > 0 ) { $enumRes = \CIBlockPropertyEnum::GetList( [], ['IBLOCK_ID' => $iblockId, 'CODE' => 'MISSING_AT_SOURCE', 'XML_ID' => "Y"] ); if ($enumFields = $enumRes->Fetch()) { $properties['MISSING_AT_SOURCE'] = $enumFields['ID']; } if (!empty($properties)) { $filter = array("IBLOCK_ID" => $iblockId, "!XML_ID" => $xmls); $select = array("ID", "IBLOCK_ID"); $rsElements = \CIBlockElement::GetList([], $filter, false, false, $select); while ($element = $rsElements->GetNext()) { $properties = [ "MISSING_AT_SOURCE" ]; \CIBlockElement::SetPropertyValuesEx($element["ID"], $iblockId, $properties); } } } } }