Розроблення користувацької PIM-системи для 1С-Бітрікс
Комерційні PIM-системи (Akeneo, Pimcore, Salsify) вирішують завдання управління контентом, але створюють залежність від стороннього продукту та додають архітектурну складність. Користувацька PIM на базі Бітрікс — це коли Бітрікс сам стає майстер-системою для даних продуктів: з користувацьким адміністративним інтерфейсом, логікою валідації атрибутів та можливістю синдикації на кілька каналів.
Коли потрібна користувацька PIM
Користувацька PIM виправдана, коли:
- Команда працює лише в екосистемі Бітрікс та не хоче вчити нову систему
- Бюджет не дозволяє ліцензію Akeneo Enterprise ($25 000+/рік)
- Вимоги такі специфічні, що готова PIM все одно вимагала б розширеної кастомізації
- Каталог містить 50 000–100 000 SKU (для більших обсягів варто розглянути спеціалізовану PIM)
Архітектура даних
Стандартний інфоблок Бітрікс зберігає властивості в b_iblock_element_property за моделлю EAV. При великій кількості атрибутів (50+) та високому трафіку це створює проблеми з продуктивністю. Для користувацької PIM використовуємо гібридний підхід:
Фіксовані атрибути (назва, опис, зображення) — у полях інфоблоку та властивостях з MULTIPLE = 'N'.
Динамічні атрибути (специфічні для категорії: для телефонів — RAM/ROM, для одягу — склад тканини) — у користувацьких таблицях:
CREATE TABLE b_pim_attribute (
ID INT AUTO_INCREMENT PRIMARY KEY,
IBLOCK_SECTION_ID INT, -- до якої категорії привязаний атрибут
CODE VARCHAR(100) NOT NULL,
NAME VARCHAR(255) NOT NULL,
TYPE ENUM('string','number','boolean','list','multilist') DEFAULT 'string',
IS_REQUIRED TINYINT(1) DEFAULT 0,
IS_FILTERABLE TINYINT(1) DEFAULT 0,
SORT INT DEFAULT 100,
INDEX idx_section (IBLOCK_SECTION_ID)
);
CREATE TABLE b_pim_attribute_value (
ID INT AUTO_INCREMENT PRIMARY KEY,
PRODUCT_ID INT NOT NULL, -- ID елемента інфоблоку
ATTRIBUTE_ID INT NOT NULL,
VALUE_STRING VARCHAR(1000),
VALUE_NUMBER DECIMAL(15,4),
VALUE_BOOLEAN TINYINT(1),
INDEX idx_product_attr (PRODUCT_ID, ATTRIBUTE_ID)
);
CREATE TABLE b_pim_attribute_option (
ID INT AUTO_INCREMENT PRIMARY KEY,
ATTRIBUTE_ID INT NOT NULL,
VALUE VARCHAR(500) NOT NULL,
SORT INT DEFAULT 100
);
Цей підхід дозволяє встановлювати атрибути на рівні категорії без зміни схеми інфоблоку.
Адміністративний інтерфейс редагування атрибутів
Користувацький адміністративний розділ на базі компоненту bitrix:main.ui.grid:
// /local/modules/company.pim/admin/attributes.php
require_once $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_admin_before.php';
$APPLICATION->SetTitle('PIM: Управління атрибутами');
// Список атрибутів для вибраної категорії
$sectionId = (int)$_GET['section_id'];
$connection = \Bitrix\Main\Application::getConnection();
$attributes = $connection->query(
"SELECT * FROM b_pim_attribute WHERE IBLOCK_SECTION_ID = {$sectionId} ORDER BY SORT"
);
Інтерфейс дозволяє менеджеру:
- Додавати атрибути до категорії (без змін схеми БД)
- Встановлювати тип, обов'язковість, можливість фільтрації
- Сортувати атрибути перетасуванням
- Копіювати набір атрибутів у дочірню категорію
Форма редагування товару з динамічними атрибутами
В адміністративному редакторі товару (sale.admin.order.edit або користувацька сторінка) відображайте та зберігайте атрибути поточної категорії:
function renderPimFields(int $productId, int $sectionId): string
{
$connection = \Bitrix\Main\Application::getConnection();
$attributes = $connection->query(
"SELECT a.*, v.VALUE_STRING, v.VALUE_NUMBER, v.VALUE_BOOLEAN
FROM b_pim_attribute a
LEFT JOIN b_pim_attribute_value v
ON v.ATTRIBUTE_ID = a.ID AND v.PRODUCT_ID = {$productId}
WHERE a.IBLOCK_SECTION_ID = {$sectionId}
ORDER BY a.SORT"
);
$html = '<table class="pim-attributes">';
while ($attr = $attributes->fetch()) {
$html .= '<tr>';
$html .= '<td><label>' . htmlspecialchars($attr['NAME'])
. ($attr['IS_REQUIRED'] ? ' <span class="req">*</span>' : '')
. '</label></td>';
$html .= '<td>' . renderAttributeInput($attr) . '</td>';
$html .= '</tr>';
}
$html .= '</table>';
return $html;
}
function savePimFields(int $productId, array $postData): void
{
$connection = \Bitrix\Main\Application::getConnection();
foreach ($postData as $attrId => $value) {
$attrId = (int)$attrId;
$existing = $connection->query(
"SELECT ID FROM b_pim_attribute_value
WHERE PRODUCT_ID = {$productId} AND ATTRIBUTE_ID = {$attrId}"
)->fetch();
$attr = $connection->query(
"SELECT TYPE FROM b_pim_attribute WHERE ID = {$attrId}"
)->fetch();
$field = match($attr['TYPE']) {
'number' => 'VALUE_NUMBER',
'boolean' => 'VALUE_BOOLEAN',
default => 'VALUE_STRING',
};
$safeVal = $connection->getSqlHelper()->forSql($value);
if ($existing) {
$connection->query(
"UPDATE b_pim_attribute_value
SET {$field} = '{$safeVal}'
WHERE ID = {$existing['ID']}"
);
} else {
$connection->query(
"INSERT INTO b_pim_attribute_value
(PRODUCT_ID, ATTRIBUTE_ID, {$field})
VALUES ({$productId}, {$attrId}, '{$safeVal}')"
);
}
}
}
Валідація атрибутів
Обов'язкові атрибути перевіряються при збереженні товару:
function validatePimAttributes(int $productId, int $sectionId): array
{
$errors = [];
$connection = \Bitrix\Main\Application::getConnection();
$required = $connection->query(
"SELECT a.ID, a.NAME FROM b_pim_attribute a
LEFT JOIN b_pim_attribute_value v
ON v.ATTRIBUTE_ID = a.ID AND v.PRODUCT_ID = {$productId}
WHERE a.IBLOCK_SECTION_ID = {$sectionId}
AND a.IS_REQUIRED = 1
AND v.ID IS NULL"
);
while ($row = $required->fetch()) {
$errors[] = 'Не заповнений обов\'язковий атрибут: ' . $row['NAME'];
}
return $errors;
}
Синдикація на кілька каналів
Користувацька PIM дозволяє експортувати дані у різні формати для різних каналів:
-
Yandex.Market YML — генератор фіду читає
b_pim_attribute_valueта маппить у стандартні елементи YML - OZON / Wildberries — JSON-шаблони для кожного маркетплейсу
-
Google Merchant — XML-фід з атрибутами
g:brand,g:gtinта ін.
function buildYmlOffer(int $productId): array
{
$connection = \Bitrix\Main\Application::getConnection();
$values = $connection->query(
"SELECT a.CODE, v.VALUE_STRING, v.VALUE_NUMBER
FROM b_pim_attribute_value v
JOIN b_pim_attribute a ON a.ID = v.ATTRIBUTE_ID
WHERE v.PRODUCT_ID = {$productId}"
);
$params = [];
while ($row = $values->fetch()) {
$params[$row['CODE']] = $row['VALUE_STRING'] ?? $row['VALUE_NUMBER'];
}
return $params; // використовується при генерації YML
}
Тривалість реалізації
| Обсяг | Склад | Тривалість |
|---|---|---|
| Базова PIM (динамічні атрибути, форма редагування) | Таблиці + форма + збереження | 2–3 тижня |
| PIM з валідацією, UI сортування, копіюванням атрибутів | Повнофункціональний admin UI + JS-сортування | 4–5 тижнів |
| PIM + синдикація (YML, маркетплейси) + масове редагування | Експортери + bulk edit таблиця | 6–8 тижнів |
Користувацька PIM — правильний вибір для команд, які вже працюють в Бітріксі. Основний ризик — надмірне ускладнення: потрібно чітко зафіксувати вимоги перед початком розроблення та не намагатися відтворити Akeneo всередині Бітрікс.







