Каталог из 50 000 SKU с тремя языками и десятками атрибутов — стандартная задача для Битрикс. Но когда данные распределены по разным системам, рассинхронизация цен и дубли описаний становятся рутиной. Product Information Management как единый источник данных решает эту проблему. Pimcore берёт на себя централизованное управление всеми данными о продукте: от атрибутов до медиа. Мы внедрили Pimcore в десятках проектов и знаем, как сделать интеграцию надёжной и производительной.
Проблемы, которые решаем
Рассинхронизация данных — товары обновляются в Pimcore, но не попадают в Битрикс. Агенты с веб-хуками устраняют задержки. Дублирование контента — одни и те же описания правят в двух системах. Единый источник — Pimcore Data Objects. Потеря медиафайлов — изображения хранятся в DAM, а не в папках Битрикс. Автоматическая загрузка с кэшированием. Сложная структура товаров — варианты, модификации, многоязычность. Pimcore поддерживает локализованные поля и связи.
Например, в проекте с 50 000 SKU и 3 языками мы сократили время обновления каталога с 2 дней до 15 минут за счёт инкрементальной синхронизации. Экономия бюджета на поддержку составила порядка 400 000 руб. в год. Стоимость интеграции для типового проекта начинается от 150 000 руб.
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. Асинхронная обработка через очередь повышает пропускную способность. Мы также применяем буферизацию запросов при массовых обновлениях.
Процесс работы
- Аналитика: изучаем структуру данных Pimcore, маппим поля с инфоблоком.
- Проектирование: определяем агенты, очереди, кэширование.
- Реализация: пишем клиент REST API, агента, веб-хуки, обработку медиа.
- Тестирование: проверяем синхронизацию на копии каталога.
- Деплой: переносим на продакшен, настраиваем мониторинг.
Объём работ и сроки
| Объём | Состав | Срок |
|---|---|---|
| 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 и сертификат Битрикс для вашего проекта.







