Development of a product rating system for 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Development of Product Rating System in 1C-Bitrix

Product rating is an aggregated score that affects ranking in catalog, is displayed in product cards and lists. 1C-Bitrix has a vote module (voting), but for product ratings it's excessive and poorly integrated with catalog. More practical to store ratings in separate table and aggregate value into infoblock property.

Rating Storage Architecture

Table b_product_vote for individual ratings:

Field Type Purpose
ID int Primary key
PRODUCT_ID int Product ID
USER_ID int User ID (NULL = guest)
IP varchar(45) IP address (for guests and anti-gaming)
RATING tinyint Rating 1–5
CREATED_AT datetime When voted

ORM class ProductVoteTable inherits from \Bitrix\Main\ORM\Data\DataManager.

In product infoblock add two numeric properties:

  • RATING_AVG — average rating (float, updated after each vote)
  • RATING_COUNT — number of ratings

This allows sorting and filtering by rating via standard CIBlockElement::GetList() without JOIN.

Voting Algorithm

Voting is implemented via AJAX request. Component outputs form with stars, click sends POST to controller:

// 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();

// Check: already voted?
$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;
}

For authorized users check by USER_ID. For guests — by IP. Authorized user can change their rating (update existing record instead of creating new).

Aggregated Rating Recalculation

After each vote update aggregates:

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 is faster than Update() on whole element — it updates only specified properties.

Visualization: Star Widget

On frontend rating displays as SVG stars. Partial fill logic: for rating 4.3, four stars are filled completely, fifth at 30%. Implemented via CSS-clip or SVG-gradient with width proportional to fractional part.

Component for displaying rating in product card and list takes parameters:

$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'],
]);

Anti-gaming Protection

IP-based limits work for guests but not organized manipulation:

  • For authorized — one rating per product (strict USER_ID + PRODUCT_ID check).
  • Limit: cannot vote for product that was never viewed (check via b_stat_session or custom views table).
  • Optional: allow voting only for users who purchased product (similar to review verification).

Development Timeframe

Scope Components Duration
Basic ORM model, AJAX voting, star widget, aggregation to property 3–4 days
Full Rating change, anti-gaming protection, catalog sorting by rating, voting history 5–7 days