Розробка системи рейтингів товарів у 1С-Бітрикс
Рейтинг товара — агрегована оцінка, яка впливає на ранжування в каталозі, виводиться в карточці та в списках. У 1С-Бітрикс є модуль vote (голосування), але для рейтингу товарів він надлишковий та погано інтегрується з каталогом. Практичніше зберігати оцінки в окремій таблиці та агрегувати значення у властивість інфоблока.
Архітектура зберігання оцінок
Таблиця b_product_vote для індивідуальних оцінок:
| Поле | Тип | Призначення |
|---|---|---|
| ID | int | Первинний ключ |
| PRODUCT_ID | int | ID товара |
| USER_ID | int | ID користувача (NULL = гість) |
| IP | varchar(45) | IP-адреса (для гостей та захисту від накрутки) |
| RATING | tinyint | Оцінка 1–5 |
| CREATED_AT | datetime | Коли проголосував |
ORM-клас ProductVoteTable наслідується від \Bitrix\Main\ORM\Data\DataManager.
В інфоблоці товарів додаються дві числові властивості:
-
RATING_AVG— середня оцінка (float, оновлюється після кожного голосування) -
RATING_COUNT— кількість оцінок
Це дозволяє сортувати та фільтрувати за рейтингом через стандартний CIBlockElement::GetList() без JOIN.
Алгоритм голосування
Голосування реалізується через AJAX-запит. Компонент відает форму зі зірочками, клік відправляє POST на контролер:
// local/ajax/product_vote.php
\Bitrix\Main\Loader::includeModule('main');
\Bitrix\Main\Loader::includeModule('catalog');
$productId = (int)($_POST['product_id'] ?? 0);
$rating = (int)($_POST['rating'] ?? 0);
if ($rating < 1 || $rating > 5 || $productId <= 0) {
echo json_encode(['success' => false, 'error' => 'invalid_data']);
exit;
}
$userId = $GLOBALS['USER']->GetID() ?: null;
$ip = \Bitrix\Main\Context::getCurrent()->getRequest()->getRemoteAddress();
// Перевірка: вже голосував?
$existing = ProductVoteTable::getList([
'filter' => ['=PRODUCT_ID' => $productId, '=USER_ID' => $userId ?: false, '=IP' => $ip],
'limit' => 1,
])->fetch();
if ($existing && $userId === null) {
echo json_encode(['success' => false, 'error' => 'already_voted']);
exit;
}
Для авторизованих користувачів перевіряємо по USER_ID. Для гостей — по IP. Авторизований користувач може змінити свою оцінку (оновлюємо існуючий запис замість створення нового).
Перерахунок агрегованого рейтингу
Після кожного голосування оновлюємо агрегати:
function updateProductRating(int $productId): void
{
$conn = \Bitrix\Main\Application::getConnection();
$row = $conn->query(
"SELECT AVG(RATING) as AVG_RATING, COUNT(*) as CNT
FROM b_product_vote
WHERE PRODUCT_ID = {$productId}"
)->fetch();
\CIBlockElement::SetPropertyValuesEx($productId, false, [
'RATING_AVG' => round((float)$row['AVG_RATING'], 2),
'RATING_COUNT' => (int)$row['CNT'],
]);
}
SetPropertyValuesEx працює швидше, ніж Update() всього елемента — він оновлює тільки вказані властивості.
Візуалізація: віджет зі зірочками
На фронтенді рейтинг відображається як набір SVG-зірочок. Логіка часткового заповнення: для оцінки 4.3 чотири зірочки заповнені повністю, п'ята — на 30%. Реалізується через CSS-clip або SVG-gradient з шириною, пропорційною дробовій частині.
Компонент для виводу рейтингу в карточці товара та в списку приймає параметри:
$APPLICATION->IncludeComponent('custom:product.rating', '', [
'PRODUCT_ID' => $arResult['ID'],
'SHOW_FORM' => $USER->IsAuthorized() ? 'Y' : 'N',
'CURRENT_RATING' => $arResult['PROPERTIES']['RATING_AVG']['VALUE'],
'VOTE_COUNT' => $arResult['PROPERTIES']['RATING_COUNT']['VALUE'],
]);
Захист від накрутки
Обмеження по IP добре для гостей, але не для організованих накруток:
- Для авторизованих — одна оцінка на товар (жорстка перевірка по
USER_ID + PRODUCT_ID). - Обмеження: не можна голосувати за товар, який ніколи не переглядався (перевірка через
b_stat_sessionабо користувацьку таблицю переглядів). - Опціонально: дозволити голосування тільки користувачам, які купили товар (аналогічно верифікації в системі відгуків).
Терміни розробки
| Масштаб | Склад | Термін |
|---|---|---|
| Базовий | ORM-модель, AJAX-голосування, віджет зі зірочками, агрегація у властивість | 3–4 дні |
| Повний | Зміна оцінки, захист від накрутки, сортування в каталозі за рейтингом, історія голосувань | 5–7 днів |







