Integration of 1C-Bitrix with Pimcore PIM System
Pimcore is an open-source platform combining PIM (product management), DAM (digital assets), and CMS. Unlike Akeneo, Pimcore can be deployed on its own server without cloud subscription, which is important for projects with closed infrastructure. Integration with Bitrix is built through Pimcore REST API or direct database interaction when both systems are on the same network.
Pimcore Data Objects and REST API
In Pimcore, products are stored as Data Objects — structured objects with fields (Field Definitions). For each object class (Product, Variant, Category), Pimcore automatically generates a 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} — media files
GET /api/asset-list?q={"type":"image"}
Authentication is via Basic Auth or API Key in header:
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 Structure in Pimcore
A Data Object class Product contains fields defined in the Pimcore administrative interface. Typical e-commerce structure:
{
"id": 1234,
"className": "Product",
"elements": [
{"name": "sku", "type": "input", "value": "PROD-001"},
{"name": "name", "type": "localized", "value": {"ru": "Title", "en": "Name"}},
{"name": "description", "type": "wysiwyg", "value": "<p>Description...</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"}}
]
}
Mapping and Synchronization in Bitrix
The agent reads objects from Pimcore page by page and updates/creates infoblock elements:
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)) {
// Reset for next full cycle
\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
{
// Extract field value from Data Object structure
$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';
// Find by article
$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
$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);
}
// Main image
$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 Webhooks for Real-time Updates
Pimcore supports webhooks when Data Objects change. Configure in Pimcore → Settings → Webhooks:
- Event:
pimcore.dataobject.postUpdate - URL:
https://bitrix-site.ru/local/pimcore-webhook.php - Secret for signature verification
Handler on Bitrix side:
// /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);
Implementation Timeline
| Scope | Components | Timeline |
|---|---|---|
| 1,000–10,000 SKU, basic mapping | Client + page loading agent | 1–2 weeks |
| 50,000+ SKU + media files + categories | Queue + asset cache + webhooks | 3–5 weeks |
| Bilingual content + product variants | Localized fields + trade offers | add 1–2 weeks |







