Developing a product review 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 Review System in 1C-Bitrix

1C-Bitrix has a built-in forum mechanism (forum) and component bitrix:forum.reviews that can technically serve as a review system. However, most projects abandon it: excessive forum structure for simple reviews, complex moderation setup, poor integration with product cards. In practice, reviews are built either on infoblocks or ORM models from scratch.

Two Approaches to Review Storage

Approach 1: Review Infoblock. Quick, no development, but limited. Create "Reviews" infoblock with properties: PRODUCT_ID (product binding), RATING (number), ADVANTAGES, DISADVANTAGES, COMMENT. Infoblock element = one review. For product binding use "Infoblock Element" property or simple numeric field with product ID.

Output — component bitrix:catalog.reviews (if available in template) or custom component based on CIBlockElement::GetList() with filter PROPERTY_PRODUCT_ID = $productId.

Approach 2: Custom ORM Model. More flexible: transactions, complex queries, extensibility. Table b_product_review:

Field Type Purpose
ID int auto_increment Primary key
PRODUCT_ID int Product ID in infoblock
USER_ID int User ID (NULL for guests)
AUTHOR_NAME varchar(255) Author name
AUTHOR_EMAIL varchar(255) Email for notifications
RATING tinyint Rating 1–5
ADVANTAGES text Advantages
DISADVANTAGES text Disadvantages
COMMENT text Main text
STATUS enum PENDING, APPROVED, REJECTED
CREATED_AT datetime Creation date
IS_VERIFIED_PURCHASE tinyint(1) Did user purchase this product

ORM class inherits from \Bitrix\Main\ORM\Data\DataManager — get standard D7 API for queries, add, update.

Purchase Verification

One of key trust indicators for a review is "Verified Purchase". To set IS_VERIFIED_PURCHASE flag when submitting review, check order history:

function isVerifiedPurchase(int $userId, int $productId): bool
{
    $orders = \Bitrix\Sale\OrderTable::getList([
        'filter' => ['=USER_ID' => $userId, '=STATUS_ID' => 'F'],
        'select' => ['ID'],
    ]);
    $orderIds = array_column(iterator_to_array($orders), 'ID');

    if (empty($orderIds)) {
        return false;
    }

    $basket = \Bitrix\Sale\BasketTable::getList([
        'filter' => [
            '=ORDER_ID' => $orderIds,
            '=PRODUCT_ID' => $productId,
        ],
        'select' => ['ID'],
        'limit'  => 1,
    ])->fetch();

    return (bool)$basket;
}

Status 'F' is completed order. Check only completed to filter out cancelled and unpaid.

Review Moderation

New reviews get PENDING status. In admin area create custom page (or section in local/admin/) with review table and "Approve" / "Reject" buttons. On status change:

  • APPROVED → review becomes visible on site, product average rating is recalculated.
  • REJECTED → review hidden, optionally send email to author.

Notification about new review for moderator — email event REVIEW_NEW_PENDING, template in Settings → Email Events.

Product Rating Recalculation

After approving or deleting review, recalculate average rating and save to product property (e.g., AVERAGE_RATING numeric type). Speeds up rating display on catalog pages — no JOIN with reviews table on each query.

function recalculateProductRating(int $productId): void
{
    $result = \Bitrix\Main\Application::getConnection()->query(
        "SELECT AVG(RATING) as AVG_RATING, COUNT(*) as CNT
         FROM b_product_review
         WHERE PRODUCT_ID = {$productId} AND STATUS = 'APPROVED'"
    )->fetch();

    \CIBlockElement::SetPropertyValuesEx($productId, false, [
        'AVERAGE_RATING' => round((float)$result['AVG_RATING'], 1),
        'REVIEW_COUNT'   => (int)$result['CNT'],
    ]);
}

Called from event handler on review status change.

Anti-spam and Limits

  • Check for duplicate reviews: one user — one review per product (check by USER_ID + PRODUCT_ID or by AUTHOR_EMAIL + PRODUCT_ID for guests).
  • CAPTCHA for guests (component bitrix:main.captcha).
  • Rate limiting: one IP cannot submit more than 3 reviews per hour (via \Bitrix\Main\Data\Cache or table with timestamps).

Development Timeframe

Scope Components Duration
Basic ORM model, form, output, moderation in personal account 4–6 days
Full Purchase verification, rating recalculation, notifications, anti-spam, admin section 8–12 days