Интеграция Pimcore с 1С-Битрикс: синхронизация каталога товаров

Когда каталог товаров распределён по разным системам, рассинхронизация цен и дубли описаний становятся рутиной. Мы разрабатываем интеграцию Pimcore с 1С-Битрикс, создавая единый источник данных для вашего каталога. Наша команда реализует проект под ключ — от аудита до сопровождения, обеспечивая надёжную и производительную синхронизацию.
Услуги, которые мы предлагаем
Показано 1 из 1Все 1626 услуг
Интеграция Pimcore с 1С-Битрикс: синхронизация каталога товаров
Средний
~1-2 недели

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1532
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1045
  • Разработка виджета онлайн-записи на прием для медицинского центра
    Разработка виджета онлайн-записи на прием для медицинского центра
    820
  • Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    Разработка на базе 1С Предприятие для компании МИРСАНБЕЛ
    915
  • Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    Разработка сайта на CRM Битрикс24 для компании DOLBIMBY
    853
  • Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    Разработка на базе Битрикс24 для компании ТЕХНОТОРГКОМПЛЕКС
    1208

Каталог из 50 000 SKU с тремя языками и десятками атрибутов — стандартная задача для Битрикс. Но когда данные распределены по разным системам, рассинхронизация цен и дубли описаний становятся рутиной. Product Information Management как единый источник данных решает эту проблему. Pimcore берёт на себя централизованное управление всеми данными о продукте: от атрибутов до медиа. Мы внедрили Pimcore в десятках проектов и знаем, как сделать интеграцию надёжной и производительной.

Проблемы, которые решаем

Рассинхронизация данных — товары обновляются в Pimcore, но не попадают в Битрикс. Агенты с веб-хуками устраняют задержки. Дублирование контента — одни и те же описания правят в двух системах. Единый источник — Pimcore Data Objects. Потеря медиафайлов — изображения хранятся в DAM, а не в папках Битрикс. Автоматическая загрузка с кэшированием. Сложная структура товаров — варианты, модификации, многоязычность. Pimcore поддерживает локализованные поля и связи.

Например, в проекте с 50 000 SKU и 3 языками мы сократили время обновления каталога с 2 дней до 15 минут за счёт инкрементальной синхронизации. Экономия бюджета на поддержку составила порядка $3.6k–5.2k. в год. Стоимость интеграции для типового проекта начинается от $1.4k–1.9k.

Pimcore vs Akeneo: почему Pimcore выгоднее

Pimcore — open-source, без облачной подписки. Вы контролируете инфраструктуру. Он включает DAM и CMS, что снижает стоимость интеграции. Akeneo требует отдельных решений для медиа. По производительности Pimcore выигрывает при больших объёмах: REST API отвечает за 200 мс на 100 000 товаров, что в 2 раза быстрее Akeneo в аналогичных тестах.

Параметр Pimcore Akeneo
Лицензия open-source подписка (cloud/on-prem)
Встроенный DAM да нет
RPS на 100 000 товаров 500 запросов/с 200 запросов/с
Кастомизация Data Objects любая ограниченная

Помимо производительности, Pimcore даёт гибкость кастомизации Data Objects и встроенный DAM. Это сокращает затраты на дополнительные интеграции. Экономия времени на обновлении каталога может составлять до 90%.

Pimcore Data Objects: API и структура

В Pimcore продукты — это Data Objects с полями, определёнными в админке. Для каждого класса генерируется REST API:

GET /api/objects?objectClass=Product&limit=100&offset=0
GET /api/object/{id}
GET /api/object-list?objectClass=Product&q={"active":true}
GET /api/asset/{id} — медиафайлы
GET /api/asset-list?q={"type":"image"}

Аутентификация — Basic Auth или API-ключ:

class PimcoreClient {
    private string $baseUrl;
    private array $authHeaders;

    public function __construct(string $baseUrl, string $apiKey) {
        $this->baseUrl = rtrim($baseUrl, '/');
        $this->authHeaders = ['X-API-Key' => $apiKey];
    }

    public function getObjects(
        string $class,
        int $offset = 0,
        int $limit = 100,
        ?string $filter = null
    ): array {
        $http = new \Bitrix\Main\Web\HttpClient();
        foreach ($this->authHeaders as $k => $v) {
            $http->setHeader($k, $v);
        }
        $url = $this->baseUrl . '/api/objects' . '?objectClass=' . urlencode($class) . '&offset=' . $offset . '&limit=' . $limit;
        if ($filter) {
            $url .= '&q=' . urlencode($filter);
        }
        $response = $http->get($url);
        $data = json_decode($response, true);
        return $data['data'] ?? [];
    }

    public function getAsset(int $assetId): ?string {
        $http = new \Bitrix\Main\Web\HttpClient();
        foreach ($this->authHeaders as $k => $v) {
            $http->setHeader($k, $v);
        }
        return $http->get($this->baseUrl . '/api/asset/' . $assetId . '/download') ?: null;
    }
}

Структура Data Object

Data Object класса Product содержит поля, определённые в админке. Типичная структура:

{
  "id": 1234,
  "className": "Product",
  "elements": [
    {
      "name": "sku",
      "type": "input",
      "value": "PROD-001"
    },
    {
      "name": "name",
      "type": "localized",
      "value": {
        "ru": "Название",
        "en": "Name"
      }
    },
    {
      "name": "description",
      "type": "wysiwyg",
      "value": "<p>Описание...</p>"
    },
    {
      "name": "price",
      "type": "numeric",
      "value": 1500.00
    },
    {
      "name": "weight",
      "type": "numeric",
      "value": 0.5
    },
    {
      "name": "active",
      "type": "checkbox",
      "value": true
    },
    {
      "name": "mainImage",
      "type": "image",
      "value": {
        "id": 567,
        "type": "asset"
      }
    },
    {
      "name": "gallery",
      "type": "imageGallery",
      "value": [
        {
          "id": 568
        },
        {
          "id": 569
        }
      ]
    },
    {
      "name": "category",
      "type": "manyToOne",
      "value": {
        "id": 890,
        "className": "Category"
      }
    }
  ]
}

Маппинг и синхронизация в Битрикс

Агент читает объекты постранично и обновляет инфоблок:

function syncPimcoreProductsAgent(): string {
    $client = new PimcoreClient(PIMCORE_URL, PIMCORE_API_KEY);
    $offset = (int)\Bitrix\Main\Config\Option::get('pimcore_sync', 'offset', 0);
    $limit = 50;
    $products = $client->getObjects('Product', $offset, $limit, '{"active":true}');
    if (empty($products)) {
        \Bitrix\Main\Config\Option::set('pimcore_sync', 'offset', 0);
        return __FUNCTION__ . '();';
    }
    foreach ($products as $pimProduct) {
        importPimcoreProduct($pimProduct, $client);
    }
    \Bitrix\Main\Config\Option::set('pimcore_sync', 'offset', $offset + $limit);
    return __FUNCTION__ . '();';
}

function importPimcoreProduct(array $product, PimcoreClient $client): void {
    $getField = static function (array $elements, string $name) {
        foreach ($elements as $el) {
            if ($el['name'] === $name) {
                return $el['value'];
            }
        }
        return null;
    };
    $elements = $product['elements'];
    $sku = $getField($elements, 'sku');
    $nameRu = $getField($elements, 'name')['ru'] ?? '';
    $descRu = $getField($elements, 'description') ?? '';
    $active = $getField($elements, 'active') ? 'Y' : 'N';
    $existing = CIBlockElement::GetList(
        [],
        ['IBLOCK_ID' => CATALOG_IBLOCK_ID, 'PROPERTY_CML2_ARTICLE' => $sku]
    )->Fetch();
    $fields = [
        'IBLOCK_ID' => CATALOG_IBLOCK_ID,
        'ACTIVE' => $active,
        'NAME' => $nameRu,
        'DETAIL_TEXT' => $descRu,
        'DETAIL_TEXT_TYPE' => 'html',
    ];
    $props = ['CML2_ARTICLE' => $sku];
    $price = $getField($elements, 'price');
    $iblockEl = new CIBlockElement();
    if ($existing) {
        $productId = $existing['ID'];
        $iblockEl->Update($productId, $fields);
        CIBlockElement::SetPropertyValuesEx($productId, CATALOG_IBLOCK_ID, $props);
    } else {
        $productId = $iblockEl->Add($fields);
        if ($productId) {
            CIBlockElement::SetPropertyValuesEx($productId, CATALOG_IBLOCK_ID, $props);
        }
    }
    if ($productId && $price !== null) {
        updateCatalogPrice($productId, (float)$price);
    }
    $mainImage = $getField($elements, 'mainImage');
    if ($productId && isset($mainImage['id'])) {
        importPimcoreAsset($productId, (int)$mainImage['id'], $client, 'DETAIL_PICTURE');
    }
}

function importPimcoreAsset(
    int $productId,
    int $assetId,
    PimcoreClient $client,
    string $field
): void {
    $cacheKey = "pimcore_asset_{$assetId}";
    $cache = \Bitrix\Main\Data\Cache::createInstance();
    if ($cache->initCache(86400 * 30, $cacheKey, '/pimcore/assets')) {
        $bitrixFileId = $cache->getVars()['file_id'];
    } else {
        $content = $client->getAsset($assetId);
        $tmpPath = sys_get_temp_dir() . "/pim_{$assetId}.jpg";
        file_put_contents($tmpPath, $content);
        $bitrixFile = \CFile::MakeFileArray($tmpPath);
        $bitrixFileId = \CFile::SaveFile($bitrixFile, 'iblock');
        unlink($tmpPath);
        $cache->startDataCache(86400 * 30, $cacheKey, '/pimcore/assets');
        $cache->endDataCache(['file_id' => $bitrixFileId]);
    }
    if ($field === 'DETAIL_PICTURE') {
        \CIBlockElement::Update($productId, ['DETAIL_PICTURE' => $bitrixFileId]);
    } else {
        \CIBlockElement::SetPropertyValues($productId, CATALOG_IBLOCK_ID, $bitrixFileId, 'MORE_PHOTO');
    }
}

Веб-хуки и отказоустойчивость

Pimcore поддерживает веб-хуки при изменении Data Object. Настраивается в Pimcore → Settings → Webhooks:

  • Event: pimcore.dataobject.postUpdate
  • URL: https://bitrix-site.ru/local/pimcore-webhook.php
  • Secret для верификации подписи

Обработчик:

// /local/pimcore-webhook.php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_PIMCORE_SIGNATURE'] ?? '';
if (!verifyPimcoreSignature($payload, $signature, PIMCORE_WEBHOOK_SECRET)) {
    http_response_code(403);
    die('Invalid signature');
}
$data = json_decode($payload, true);
if ($data['className'] === 'Product') {
    $client = new PimcoreClient(PIMCORE_URL, PIMCORE_API_KEY);
    $product = $client->getObjects('Product', 0, 1, json_encode(['id' => $data['id']]));
    if (!empty($product[0])) {
        importPimcoreProduct($product[0], $client);
    }
}
http_response_code(200);

Веб-хуки срабатывают сразу после изменения объекта. Мы настраиваем верификацию подписи: без неё хуки не принимаются. Это защищает от подделки. Если хук не обработан — агент подхватит изменения в ближайший цикл. Дополнительно логируются все ошибки с отправкой уведомлений администратору.

Метод Задержка Нагрузка на API Сложность реализации
Агент (по расписанию) до 1 минуты низкая (постранично) низкая
Веб-хуки мгновенно средняя (под каждое изменение) средняя (требуется обработка)

Выбор зависит от требований к актуальности. Для большинства проектов достаточно агента, для real-time — добавляем веб-хуки.

Как обеспечить быструю синхронизацию данных

Для производительности используйте инкрементальную загрузку и кэширование медиа. Агенты с постраничной выборкой (50-100 объектов) не нагружают REST API. Асинхронная обработка через очередь повышает пропускную способность. Мы также применяем буферизацию запросов при массовых обновлениях.

Процесс работы

  1. Аналитика: изучаем структуру данных Pimcore, маппим поля с инфоблоком.
  2. Проектирование: определяем агенты, очереди, кэширование.
  3. Реализация: пишем клиент REST API, агента, веб-хуки, обработку медиа.
  4. Тестирование: проверяем синхронизацию на копии каталога.
  5. Деплой: переносим на продакшен, настраиваем мониторинг.

Объём работ и сроки

Объём Состав Срок
1 000–10 000 SKU, базовый маппинг Клиент + агент постраничной загрузки 1–2 недели
50 000+ SKU + медиафайлы + категории Очередь + кеш ассетов + вебхуки 3–5 недель
Двуязычный контент + product variants Локализованные поля + торговые предложения добавить 1–2 недели

Стоимость рассчитывается индивидуально. Мы оценим проект за 1 день. Закажите интеграцию — свяжитесь с нами, и наши инженеры подготовят коммерческое предложение. Более 50 успешных интеграций — гарантируем качество. Получите консультацию уже сегодня.

Чек-лист типичных ошибок

  • Не настроено кэширование ассетов — каждый запрос скачивает файл заново. Решение: кеш в Битрикс с TTL 30 дней.
  • Отсутствует верификация веб-хуков — риск подделки. Всегда проверяйте подпись.
  • Слишком большой лимит запросов — REST API Pimcore падает. Используйте пагинацию по 50-100 объектов.
  • Не учитываются торговые предложения — если в Pimcore есть варианты, мапьте их на торговые предложения Битрикс.

Свяжитесь с нами для консультации. Получите SLA и сертификат Битрикс для вашего проекта.