Разработка фильтрации по рейтингу и отзывам 1С-Битрикс
Фильтр по рейтингу («только товары с оценкой 4+») и по наличию отзывов — нишевый, но востребованный инструмент в каталогах с большим количеством пользовательских рецензий. Стандартный умный фильтр такую фильтрацию не поддерживает: рейтинг и отзывы хранятся не в свойствах инфоблока, а в отдельных таблицах модуля vote или в кастомных полях.
Где хранятся рейтинги в 1С-Битрикс
Модуль vote (голосования): рейтинг через CVote. Таблицы b_vote, b_vote_event, b_vote_answer. Сложная структура, оптимизированная под опросы, а не под рейтинги товаров.
Пользовательские поля (UF): рейтинг как числовое UF-поле элемента инфоблока (UF_RATING, UF_REVIEW_COUNT). Самый гибкий подход — обновляется при добавлении/изменении отзыва, фильтруется как обычное свойство.
Таблица отзывов: модуль blog или кастомная таблица. Требует JOIN для фильтрации.
Рекомендуемый подход для новых проектов — UF-поля с денормализацией: при добавлении/изменении отзыва пересчитывается и сохраняется средний рейтинг и количество отзывов в полях элемента.
Структура UF-полей для рейтинга
// Создание UF-полей при установке
$userTypeManager = \Bitrix\Main\EventManager::getInstance();
// UF_RATING - средний рейтинг
\CUserTypeEntity::Add([
'ENTITY_ID' => 'IBLOCK_' . CATALOG_IBLOCK_ID . '_SECTION',
'FIELD_NAME' => 'UF_RATING',
'USER_TYPE_ID' => 'double',
'MANDATORY' => 'N',
'SHOW_FILTER' => 'S', // участвует в фильтрации
'SETTINGS' => ['PRECISION' => 1, 'MIN' => 0, 'MAX' => 5],
]);
// UF_REVIEW_COUNT - количество отзывов
\CUserTypeEntity::Add([
'ENTITY_ID' => 'IBLOCK_' . CATALOG_IBLOCK_ID . '_SECTION',
'FIELD_NAME' => 'UF_REVIEW_COUNT',
'USER_TYPE_ID' => 'integer',
'MANDATORY' => 'N',
'SHOW_FILTER' => 'S',
]);
Обновление рейтинга при добавлении отзыва
AddEventHandler('blog', 'OnAfterPostAdd', 'updateProductRating');
AddEventHandler('blog', 'OnAfterPostUpdate', 'updateProductRating');
function updateProductRating($id, $fields)
{
// Получение ID товара из связи с отзывом
$productId = getProductIdFromPost($id);
if (!$productId) return;
// Пересчёт рейтинга
$reviews = getProductReviews($productId);
$count = count($reviews);
$avg = $count > 0
? array_sum(array_column($reviews, 'rating')) / $count
: 0;
// Обновление UF-полей элемента
CIBlockElement::SetPropertyValues($productId, CATALOG_IBLOCK_ID, [
'UF_RATING' => round($avg, 1),
'UF_REVIEW_COUNT' => $count,
]);
}
Фильтрация по минимальному рейтингу
// Применение фильтра рейтинга
if (!empty($_GET['min_rating'])) {
$minRating = floatval($_GET['min_rating']);
if ($minRating >= 1 && $minRating <= 5) {
$arFilter['>=UF_RATING'] = $minRating;
}
}
// Фильтр "только с отзывами"
if (!empty($_GET['has_reviews'])) {
$arFilter['>UF_REVIEW_COUNT'] = 0;
}
UI: звёздный рейтинг-фильтр
$currentMinRating = floatval($_GET['min_rating'] ?? 0);
?>
<div class="filter-block filter-block--rating">
<h3 class="filter-block__title">Рейтинг</h3>
<div class="rating-filter">
<?php for ($stars = 5; $stars >= 1; $stars--): ?>
<label class="rating-option <?= $currentMinRating == $stars ? 'is-active' : '' ?>">
<input type="radio" name="min_rating"
value="<?= $stars ?>"
<?= $currentMinRating == $stars ? 'checked' : '' ?>>
<span class="stars">
<?php for ($i = 1; $i <= 5; $i++): ?>
<svg class="star <?= $i <= $stars ? 'star--filled' : 'star--empty' ?>"
viewBox="0 0 24 24" width="16" height="16">
<polygon points="12,2 15.09,8.26 22,9.27 17,14.14 18.18,21.02 12,17.77 5.82,21.02 7,14.14 2,9.27 8.91,8.26"/>
</svg>
<?php endfor; ?>
<span class="stars-label">от <?= $stars ?></span>
</span>
</label>
<?php endfor; ?>
</div>
<label class="filter-toggle">
<input type="checkbox" name="has_reviews" value="1"
<?= !empty($_GET['has_reviews']) ? 'checked' : '' ?>>
<span>Только с отзывами</span>
</label>
</div>
<?php
Сортировка по рейтингу
Фильтр по рейтингу органично сочетается с сортировкой:
$sortField = htmlspecialchars($_GET['sort'] ?? 'SORT');
$sortOrder = in_array(strtoupper($_GET['order'] ?? 'ASC'), ['ASC', 'DESC'])
? strtoupper($_GET['order'])
: 'ASC';
$arSort = match ($sortField) {
'rating' => ['UF_RATING' => 'DESC'],
'reviews' => ['UF_REVIEW_COUNT' => 'DESC'],
'price' => ['CATALOG_PRICE_1' => $sortOrder],
default => ['SORT' => 'ASC'],
};
Кейс: маркетплейс электроники
Интернет-магазин электроники с 15 000 товаров. Внедрили звёздный рейтинг-фильтр и сортировку по рейтингу. UF-поля обновляются через агент каждые 30 минут (не по событию, т.к. отзывы проходят модерацию). После модерации — ручной пересчёт через кнопку в административной панели.
Результат: товары без отзывов (новинки) скрыты по умолчанию в фильтре, показываются при снятии галочки «Только с отзывами». Конверсия из фильтрованного каталога (рейтинг 4+) на 23% выше среднего.
Сроки выполнения
Фильтр по UF-рейтингу с простым UI — 1 рабочий день. Полная реализация с пересчётом рейтинга при добавлении отзыва, звёздным UI, сортировкой и агентом — 2–3 рабочих дня.







