קטלוג של 50,000 פריטים (SKU) עם שלוש שפות ועשרות מאפיינים הוא משימה סטנדרטית עבור Bitrix. אבל כאשר הנתונים מפוזרים על פני מערכות שונות, חוסר סנכרון מחירים וכפילויות בתיאורים הופכים לשגרה. ניהול מידע מוצר (Product Information Management) כמקור אמת יחיד פותר זאת. Pimcore לוקחת על עצמה ניהול מרכזי של כל נתוני המוצר: ממאפיינים ועד מדיה. יישמנו את Pimcore בעשרות פרויקטים ואנחנו יודעים איך להפוך את האינטגרציה לאמינה וביצועית.
בעיות שאנחנו פותרים
חוסר סנכרון נתונים — מוצרים מתעדכנים ב-Pimcore אבל לא מגיעים ל-Bitrix. סוכנים (Agents) עם Webhooks מבטלים עיכובים. כפילויות תוכן — אותם תיאורים נערכים בשתי מערכות. מקור אמת יחיד — Pimcore Data Objects. אובדן מדיה — תמונות מאוחסנות ב-DAM, לא בתיקיות Bitrix. טעינה אוטומטית עם מטמון (Caching). מבני מוצר מורכבים — וריאנטים, שינויים, רב-לשוניות. Pimcore תומכת בשדות מקומיים וביחסים.
לדוגמה, בפרויקט עם 50,000 פריטים ו-3 שפות, צמצמנו את זמן עדכון הקטלוג מיומיים ל-15 דקות באמצעות סנכרון מצטבר. זה הביא לחיסכון שנתי של 50,000 דולר בתמיכה.
Pimcore מול Akeneo: למה Pimcore משתלמת יותר
Pimcore היא קוד פתוח, ללא מנוי ענן. אתה שולט בתשתית. היא כוללת DAM ו-CMS, מה שמפחית את עלות האינטגרציה. Akeneo דורשת פתרונות נפרדים למדיה. מבחינת ביצועים, Pimcore מנצחת בנפחים גדולים: REST API מגיב ב-200 אלפיות השנייה עבור 100,000 מוצרים, מהיר פי 2 מ-Akeneo בבדיקות דומות.
| פרמטר | Pimcore | Akeneo |
|---|---|---|
| רישיון | קוד פתוח | מנוי (ענן/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 ממחלקה 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"} מכיל שדות המוגדרים בממשק הניהול. מבנה טיפוסי:
{
"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"
}
}
]
} מיפוי וסנכרון ב-Bitrix
סוכן קורא אובייקטים עמוד אחר עמוד ומעדכן את ה-infoblock:
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');
}
} Webhooks וסובלנות לתקלות
Pimcore תומכת ב-Webhooks על שינויים ב-Data Objects. הגדר ב-Pimcore → Settings → Webhooks:
- אירוע:
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; } } - URL:
Product - סוד (Secret) לאימות חתימה
מטפל (Handler):
// /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);
Webhooks מופעלים מיד לאחר שינוי אובייקט. אנו מגדירים אימות חתימה: בלעדיו, ה-hooks לא מתקבלים. זה מגן מפני זיוף. אם hook לא מעובד, הסוכן קולט את השינויים במחזור הבא. בנוסף, כל השגיאות מתועדות עם התראות למנהל המערכת.
| שיטה | זמן השהיה | עומס API | מורכבות יישום |
|---|---|---|---|
| סוכן (מתוזמן) | עד דקה | נמוך (עמודים) | נמוכה |
| Webhooks | מיידי | בינוני (לכל שינוי) | בינונית (דורש טיפול) |
הבחירה תלויה בדרישות הזמינות. עבור רוב הפרויקטים, סוכן מספיק; עבור זמן אמת, אנו מוסיפים Webhooks.
איך להבטיח סנכרון נתונים מהיר
לביצועים, השתמש בטעינה מצטברת ובמטמון מדיה. סוכנים עם עמודים (50-100 אובייקטים) לא מעמיסים על REST API. עיבוד אסינכרוני באמצעות תורים מגדיל את התפוקה. אנו גם משתמשים ב-buffering של בקשות לעדכונים בכמות גדולה.
תהליך
- ניתוח: לימוד מבנה הנתונים של Pimcore, מיפוי שדות ל-infoblock.
- עיצוב: הגדרת סוכנים, תורים, מטמון.
- יישום: כתיבת לקוח REST API, סוכן, Webhooks, טיפול במדיה.
- בדיקות: אימות סנכרון על עותק קטלוג.
- השקה: מעבר לייצור, הגדרת ניטור.
היקף ולוחות זמנים
| נפח | היקף | לוח זמנים |
|---|---|---|
| 1,000–10,000 פריטים, מיפוי בסיסי | לקוח + סוכן עם עמודים | 1–2 שבועות |
| 50,000+ פריטים + קבצי מדיה + קטגוריות | תור + מטמון נכסים + Webhooks | 3–5 שבועות |
| תוכן דו-לשוני + וריאנטים של מוצר | שדות מקומיים + הצעות SKU | הוסף 1–2 שבועות |
העלות מחושבת באופן אישי. אינטגרציה מתחילה מ-10,000 דולר. נבחן את הפרויקט שלך תוך יום אחד. הזמן אינטגרציה — צור קשר, והמהנדסים שלנו יכינו הצעה מסחרית. 50+ אינטגרציות מוצלחות — אנחנו מבטיחים איכות. קבל ייעוץ היום.
מה כלול באינטגרציה
- תיעוד טכני עם מפרטי API ומיפוי שדות
- גישה לסביבת פיתוח ומאגר ניהול גרסאות
- הדרכת מנהלים (עד 3 שעות מרחוק)
- תמיכה למשך 30 יום לאחר העלייה לאוויר
- סקריפטים לייבוא/ייצוא ידני בכמות גדולה
רשימת בדיקה של טעויות נפוצות
- מטמון נכסים לא מוגדר — כל בקשה מורידה את הקובץ מחדש. פתרון: מטמון ב-Bitrix עם TTL של 30 יום.
- אימות Webhook חסר — סיכון לזיוף. תמיד בדוק את החתימה.
- מגבלת בקשות גדולה מדי — REST API של Pimcore קורס. השתמש בעמודים של 50-100 אובייקטים.
- הצעות SKU לא מטופלות — אם ל-Pimcore יש וריאנטים, מפה אותם להצעות SKU של Bitrix.
איך הסנכרון מתמודד עם שגיאות?
שגיאות מתועדות ומנוסות אוטומטית. עבור תקלות קריטיות, מנהל המערכת מקבל הודעה בדוא"ל או דרך בוט. אנו כוללים גם טריגרים לסנכרון ידני בפאנל הניהול של Bitrix.
מה העלות הטיפוסית של פרויקט?
פרויקטים מתחילים ב-10,000 דולר לאינטגרציה בסיסית. מקרים מורכבים עם מדיה ותוכן רב-לשוני נעים בין 25,000 ל-50,000 דולר. צור קשר לקבלת הצעת מחיר מותאמת.
צור קשר לייעוץ. קבל SLA ותעודת Bitrix לפרויקט שלך.

