1. Главная
  2. Блог
  3. 1С-Битрикс
  4. Аспро Максимум: почему не совпадает количество отзывов и комментариев в карточке товара

Аспро Максимум: почему не совпадает количество отзывов и комментариев в карточке товара

10 июня 2026
112

В решении Аспро Максимум для 1С-Битрикс отзывы к товарам могут быть реализованы через комментарии блога. В большинстве случаев этот механизм работает корректно, но иногда в карточке товара появляется странное расхождение: во вкладке «Отзывы» отображается одно количество комментариев, а рядом со звёздами рейтинга в верхней части карточки — другое количество отзывов.

Например, во вкладке товара видно 8 опубликованных отзывов, каждый из которых имеет оценку 5 звёзд, а в верхней части карточки отображается только 7 отзывов. При этом модерация отключена, комментарии действительно опубликованы, а кеш сайта не является причиной проблемы.

Какие свойства участвуют в подсчёте отзывов

В карточке товара Аспро Максимум могут использоваться несколько разных свойств, связанных с отзывами и комментариями:

  • BLOG_COMMENTS_CNT — количество комментариев блога;
  • EXTENDED_REVIEWS_COUNT — количество расширенных отзывов;
  • EXTENDED_REVIEWS_RAITING — средний рейтинг отзывов.

Важно понимать, что это не одно и то же. Счётчик комментариев блога обновляется стандартной логикой Битрикса, а расширенные отзывы и рейтинг пересчитываются отдельной логикой шаблона Аспро Максимум.

Как проявляется проблема

На странице товара можно увидеть два разных числа. Во вкладке «Отзывы» счётчик может быть связан с количеством комментариев блога:

<span class="element-count muted font_xs rounded3">8</span>

А в верхней части карточки товара рядом со звёздами может выводиться значение свойства EXTENDED_REVIEWS_COUNT:

<span class="font_sxs">7</span>

В результате пользователь видит 8 отзывов во вкладке, но только 7 отзывов рядом с рейтингом. Это выглядит как ошибка отображения, хотя на самом деле причина находится в механизме пересчёта свойств товара.

количество отзывов и комментариев в карточке товара

Почему рейтинг 5.0 / 5 — это не количество отзывов

Отдельно стоит пояснить отображение среднего рейтинга. Если в блоке отзывов выводится значение:

<span class="count">5.0</span>
<span class="maximum_value">/5</span>

это означает не «5 отзывов из 5», а среднюю оценку по шкале от 1 до 5.

Например, если у товара 8 отзывов и каждый отзыв имеет оценку 5 звёзд, средняя оценка будет равна 5.0:

(5 + 5 + 5 + 5 + 5 + 5 + 5 + 5) / 8 = 5.0

Поэтому значение 5.0 / 5 является корректным. Количество отзывов должно выводиться отдельно.

В чём была реальная причина

Причина расхождения оказалась в том, что несколько товаров использовали один и тот же BLOG_POST_ID.

Такое может быть нормальной ситуацией. Например, в магазине есть основной товар и уценённая версия этого же товара. Логично, что отзывы у них общие, потому что фактически речь идёт об одной модели.

Но штатный механизм Аспро Максимум при пересчёте расширенных отзывов ищет товар по свойству BLOG_POST_ID и обновляет только один найденный элемент. Если товаров с таким BLOG_POST_ID несколько, то один товар получает актуальные значения, а остальные могут остаться со старыми значениями свойств.

В результате возникает ситуация:

BLOG_COMMENTS_CNT = 8
EXTENDED_REVIEWS_COUNT = 7

При этом в базе все 8 комментариев могут быть корректными: опубликованными, корневыми, с нужным POST_ID и заполненной оценкой.

Как проверить наличие нескольких товаров с одним BLOG_POST_ID

Для диагностики можно найти все товары, у которых используется один и тот же BLOG_POST_ID. Например, если ID поста отзывов равен 286, можно выполнить SQL-запрос:

SELECT 
  BE.ID,
  BE.NAME,
  BE.ACTIVE,
  PROP.VALUE AS BLOG_POST_ID
FROM b_iblock_element BE
INNER JOIN b_iblock_element_property PROP ON PROP.IBLOCK_ELEMENT_ID = BE.ID
WHERE PROP.VALUE = 286;

Если запрос возвращает несколько товаров, значит штатный пересчёт Аспро может обновлять только один из них. Именно из-за этого у связанных карточек товара могут отличаться значения EXTENDED_REVIEWS_COUNT и BLOG_COMMENTS_CNT.

Почему лучше не править модуль Аспро напрямую

Технически можно изменить логику внутри модуля Аспро Максимум, чтобы он обновлял все товары с одним BLOG_POST_ID. Но это плохой вариант: при обновлении решения такие изменения могут быть потеряны.

Более безопасный способ — добавить собственный обработчик в init.php. Такой обработчик будет работать поверх штатной логики и не затрагивать файлы модуля.

Решение: пересчитывать отзывы для всех товаров с одним BLOG_POST_ID

Задача обработчика:

  • срабатывать при добавлении, обновлении и удалении комментария;
  • получать POST_ID комментария;
  • считать количество опубликованных корневых комментариев;
  • считать средний рейтинг по полю UF_ASPRO_COM_RATING;
  • находить все товары с этим же BLOG_POST_ID;
  • обновлять у всех найденных товаров свойства отзывов и рейтинга.

Код можно добавить в файл:

/local/php_interface/init.php

Если проект использует старую структуру, файл может находиться здесь:

/bitrix/php_interface/init.php

Код обработчика

<?php

// Синхронизация отзывов для всех товаров с одним BLOG_POST_ID.
// Актуально для случаев, когда основной товар и уценка используют общий пост отзывов.

AddEventHandler('blog', 'OnCommentAdd', 'customAsproReviewsSyncOnCommentAddUpdate', 1000);
AddEventHandler('blog', 'OnCommentUpdate', 'customAsproReviewsSyncOnCommentAddUpdate', 1000);

AddEventHandler('blog', 'OnBeforeCommentDelete', 'customAsproReviewsRememberPostIdBeforeDelete', 1000);
AddEventHandler('blog', 'OnCommentDelete', 'customAsproReviewsSyncOnCommentDelete', 1000);

function customAsproReviewsSyncOnCommentAddUpdate($commentID, &$arFields = null)
{
                $commentID = (int)$commentID;

                if ($commentID <= 0) {
                               return;
                }

                $postID = 0;

                if (is_array($arFields) && !empty($arFields['POST_ID'])) {
                               $postID = (int)$arFields['POST_ID'];
                }

                if ($postID <= 0 && CModule::IncludeModule('blog')) {
                               $comment = CBlogComment::GetByID($commentID);

                               if (is_array($comment) && !empty($comment['POST_ID'])) {
                                               $postID = (int)$comment['POST_ID'];
                               }
                }

                if ($postID > 0) {
                               customAsproReviewsScheduleRecalcByPostId($postID);
                }
}

function customAsproReviewsRememberPostIdBeforeDelete($commentID)
{
                $commentID = (int)$commentID;

                if ($commentID <= 0) {
                               return;
                }

                if (!CModule::IncludeModule('blog')) {
                               return;
                }

                $comment = CBlogComment::GetByID($commentID);

                if (is_array($comment) && !empty($comment['POST_ID'])) {
                               $GLOBALS['CUSTOM_ASPRO_REVIEWS_DELETED_POST_IDS'][$commentID] = (int)$comment['POST_ID'];
                }
}

function customAsproReviewsSyncOnCommentDelete($commentID)
{
                $commentID = (int)$commentID;

                if ($commentID <= 0) {
                               return;
                }

                $postID = 0;

                if (!empty($GLOBALS['CUSTOM_ASPRO_REVIEWS_DELETED_POST_IDS'][$commentID])) {
                               $postID = (int)$GLOBALS['CUSTOM_ASPRO_REVIEWS_DELETED_POST_IDS'][$commentID];
                }

                if ($postID > 0) {
                               customAsproReviewsScheduleRecalcByPostId($postID);
                }
}

function customAsproReviewsScheduleRecalcByPostId($postID)
{
                static $scheduled = array();

                $postID = (int)$postID;

                if ($postID <= 0 || isset($scheduled[$postID])) {
                               return;
                }

                $scheduled[$postID] = true;

                register_shutdown_function('customAsproReviewsRecalcByPostId', $postID);
}

function customAsproReviewsRecalcByPostId($postID)
{
                $postID = (int)$postID;

                if ($postID <= 0) {
                               return;
                }

                if (!CModule::IncludeModule('blog') || !CModule::IncludeModule('iblock')) {
                               return;
                }

                $commentsCount = 0;
                $commentsRatingSum = 0;
                $commentsRatingCount = 0;

                $resComments = CBlogComment::GetList(
                               array('ID' => 'ASC'),
                               array(
                                               'POST_ID' => $postID,
                                               'PARENT_ID' => false,
                                               'PUBLISH_STATUS' => 'P',
                               ),
                               false,
                               false,
                               array(
                                               'ID',
                                               'UF_ASPRO_COM_RATING',
                               )
                );

                while ($comment = $resComments->Fetch()) {
                               $commentsCount++;

                               $rating = (int)$comment['UF_ASPRO_COM_RATING'];

                               if ($rating > 0) {
                                               $commentsRatingCount++;
                                               $commentsRatingSum += $rating;
                               }
                }

                $ratingValue = 0;

                if ($commentsRatingCount > 0) {
                               $ratingValue = round($commentsRatingSum / $commentsRatingCount, 1);
                }

                $resElements = CIBlockElement::GetList(
                               array('ID' => 'ASC'),
                               array(
                                               'PROPERTY_BLOG_POST_ID' => $postID,
                               ),
                               false,
                               false,
                               array(
                                               'ID',
                                               'IBLOCK_ID',
                                               'NAME',
                                               'PROPERTY_BLOG_POST_ID',
                               )
                );

                while ($element = $resElements->Fetch()) {
                               CIBlockElement::SetPropertyValuesEx(
                                               (int)$element['ID'],
                                               (int)$element['IBLOCK_ID'],
                                               array(
                                                               'BLOG_COMMENTS_CNT' => $commentsCount,
                                                               'EXTENDED_REVIEWS_COUNT' => $commentsCount,
                                                               'EXTENDED_REVIEWS_RAITING' => $ratingValue,
                                               )
                               );
                }
}

Почему отдельно обработано удаление отзывов

При добавлении или обновлении комментария его POST_ID можно получить напрямую. Но при удалении комментария после события удаления запись уже может отсутствовать в базе.

Поэтому используется связка из двух событий:

  • OnBeforeCommentDelete — до удаления запоминается POST_ID комментария;
  • OnCommentDelete — после удаления выполняется пересчёт отзывов по сохранённому POST_ID.

Без такой обработки при удалении отзыва счётчики могли бы не уменьшаться.

Дополнительная доработка: сортировка отзывов по новизне

Ещё одна распространённая особенность Аспро Максимум — сортировка отзывов по умолчанию. Часто первым выбранным вариантом является «Сначала позитивные». Если у большинства отзывов одинаковая оценка, порядок отзывов может выглядеть нелогично.

Например, старый отзыв с 5 звёздами может отображаться выше нового отзыва с такой же оценкой. Чтобы пользователю было проще ориентироваться, можно сделать сортировку по умолчанию «Сначала новые».

Файл шаблона сортировки:

/bitrix/templates/aspro_max/components/bitrix/catalog.comments/catalog/bitrix/blog.post.comment/adapt/sort.php

Пример порядка сортировок:

<?php

$arAvailableSort = array(
                array(
                               'PROP' => 'DateFormated',
                               'ORDER' => 'SORT_DESC',
                               'MESSAGE' => 'DATE_DESC',
                ),
                array(
                               'PROP' => 'UF_ASPRO_COM_RATING',
                               'ORDER' => 'SORT_DESC',
                               'MESSAGE' => 'RATING_DESC',
                ),
                array(
                               'PROP' => 'UF_ASPRO_COM_RATING',
                               'ORDER' => 'SORT_ASC',
                               'MESSAGE' => 'RATING_ASC',
                ),
                array(
                               'PROP' => 'DateFormated',
                               'ORDER' => 'SORT_ASC',
                               'MESSAGE' => 'DATE_ASC',
                ),
                array(
                               'PROP' => 'UF_ASPRO_COM_LIKE',
                               'ORDER' => 'SORT_DESC',
                               'MESSAGE' => 'LIKE_DESC',
                ),
);

Также нужно убедиться, что при первой загрузке страницы фактическая сортировка совпадает с выбранным пунктом. Для этого в result_modifier.php задаётся дефолтная сортировка по дате.

Файл:

/bitrix/templates/aspro_max/components/bitrix/catalog.comments/catalog/bitrix/blog.post.comment/adapt/result_modifier.php
<?php

global $USER_FIELD_MANAGER;

$arParams['SORT_PROP'] = !empty($_SESSION['REVIEW_SORT_PROP'])
                ? $_SESSION['REVIEW_SORT_PROP']
                : 'DateFormated';

$arParams['SORT_ORDER'] = !empty($_SESSION['REVIEW_SORT_ORDER'])
                ? $_SESSION['REVIEW_SORT_ORDER']
                : 'SORT_DESC';

Дополнительная доработка: вывод года в дате отзыва

В некоторых шаблонах Аспро Максимум дата комментария выводится без года. Это неудобно, если отзывы накапливались в течение нескольких лет: пользователь видит день и месяц, но не понимает, к какому году относится отзыв.

Файл шаблона:

/bitrix/templates/aspro_max/components/bitrix/catalog.comments/catalog/bitrix/blog.post.comment/adapt/template.php

Было:

<div class="blog-comment-date"><?=FormatDate('d F, H:i', MakeTimeStamp($comment["DateFormated"]))?></div>

Стало:

<div class="blog-comment-date"><?=FormatDate('d.m.Y, H:i', MakeTimeStamp($comment["DateFormated"]))?></div>

количество отзывов и комментариев в карточке товара


Что получилось в результате

После доработки количество комментариев, количество расширенных отзывов и средний рейтинг стали синхронно обновляться у всех товаров, связанных с одним BLOG_POST_ID.

Теперь при добавлении, изменении или удалении отзыва обновляются следующие свойства:

  • BLOG_COMMENTS_CNT;
  • EXTENDED_REVIEWS_COUNT;
  • EXTENDED_REVIEWS_RAITING.

Это позволяет корректно использовать общие отзывы для нескольких карточек товара, например для основного товара и уценённой позиции.

Если в Аспро Максимум не совпадает количество отзывов и комментариев в карточке товара, сначала стоит проверить не кеш, а связь товаров с BLOG_POST_ID.

Если несколько товаров используют один и тот же пост отзывов, штатный механизм может обновлять только один товар. В таком случае лучше добавить собственный обработчик в init.php, который будет пересчитывать отзывы и рейтинг для всех связанных товаров.

Такой подход не требует изменения файлов модуля Аспро Максимум и поэтому безопаснее с точки зрения будущих обновлений решения.


Понравилась статья?

Поддержать нас рублями:

Нужна помощь? Обращайтесь!

Комментарии
Name
Email
Phone
Ваше имя
Ваш email
Оставить комментарий
Нажмите для звонка
+7 (499) 341-00-19