Клиент пришёл с жалобой: страница товара грузится 4 секунды из-за N+1 запросов при выводе отзывов. Средний рейтинг не обновлялся сутками — кеш не сбрасывался. Система отзывов тормозила каталог. Мы переписали модуль за 5 дней: денормализовали рейтинг, добавили кеширование, внедрили автоматическую модерацию. Конверсия выросла на 22%.
На одном проекте нашли 47 копий запроса SELECT * FROM reviews WHERE product_id = ? на странице каталога. После денормализации рейтинга и агрегации через Redis время загрузки снизилось с 4.2 с до 0.3 с. LCP улучшился на 60%.
Почему стоит внедрить систему отзывов?
Отзывы — социальное доказательство, влияющее на конверсию сильнее, чем описание товара. Средний рейтинг и количество отзывов отображаются в сниппетах Google через структурированные данные — это даёт преимущество в поиске. Готовая система отзывов экономит 2–3 недели разработки. Мы уже реализовали более 50 проектов в E-commerce, поэтому знаем типовые проблемы: N+1 запросы, неверная денормализация рейтинга, дубли при загрузке фотографий. Наше решение включает оптимизацию запросов, кеширование и безопасную загрузку медиа.
Как мы устраняем N+1 запросы?
Денормализуем рейтинг: добавляем поля rating_avg и rating_count в таблицу products. При одобрении отзыва запускаем recalculateRating(), который обновляет агрегаты за один запрос. Кешируем результат на час в Redis — каталог грузится без подзапросов к reviews.
| Стратегия пересчёта | Задержка обновления | Нагрузка на БД | Согласованность |
|---|---|---|---|
| Immediate (after each review) | Секунды | Средняя | Полная |
| Scheduled (cron) | Часы | Низкая | Событийная |
Рекомендуем immediate: цена — один лишний UPDATE на одобрение отзыва, зато рейтинг всегда актуален. При высоких нагрузках переключаемся на очередь через Laravel Horizon.
Как автоматизируется модерация?
Автоматическая модерация обрабатывает 80% отзывов без участия человека. Наш сервис проверяет текст по стоп-словам, анализирует рейтинг и историю пользователя. Оставшиеся 20% уходят в очередь админ-панели с быстрыми действиями: одобрить, отклонить или пометить спамом. Это в 3 раза быстрее ручной проверки.
Как настраивается список стоп-слов
Стоп-слова задаются в config/reviews.php как массив фраз. По умолчанию блокируем ссылки и контактные данные. Можно добавить свои паттерны — регулярные выражения или целые фразы. Модератор может просматривать срабатывания в админ-панели.
Техническая реализация
Модель данных
CREATE TABLE reviews (
id BIGSERIAL PRIMARY KEY,
product_id BIGINT NOT NULL REFERENCES products(id) ON DELETE CASCADE,
order_item_id BIGINT REFERENCES order_items(id),
user_id BIGINT REFERENCES users(id),
guest_name VARCHAR(100),
rating SMALLINT NOT NULL CHECK (rating BETWEEN 1 AND 5),
title VARCHAR(255),
body TEXT,
pros TEXT,
cons TEXT,
status VARCHAR(20) DEFAULT 'pending',
is_verified_purchase BOOLEAN DEFAULT FALSE,
helpful_count INT DEFAULT 0,
not_helpful_count INT DEFAULT 0,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE review_photos (
id BIGSERIAL PRIMARY KEY,
review_id BIGINT REFERENCES reviews(id) ON DELETE CASCADE,
url VARCHAR(500) NOT NULL,
sort_order SMALLINT DEFAULT 0
);
CREATE TABLE review_votes (
review_id BIGINT REFERENCES reviews(id) ON DELETE CASCADE,
user_id BIGINT REFERENCES users(id) ON DELETE CASCADE,
vote BOOLEAN NOT NULL,
PRIMARY KEY (review_id, user_id)
);
CREATE TABLE review_replies (
id BIGSERIAL PRIMARY KEY,
review_id BIGINT REFERENCES reviews(id) ON DELETE CASCADE,
user_id BIGINT REFERENCES users(id),
body TEXT NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
Мы реализуем три модели доступа к отзывам:
| Модель | Доверие | Количество отзывов | Модерация |
|---|---|---|---|
| Только покупатели | Высокое | Низкое | Ручная (минимум) |
| Авторизованные пользователи | Среднее | Среднее | Автоматическая |
| Гости | Низкое | Высокое | Ручная (все) |
Рекомендуем авторизованных пользователей с пометкой «Подтверждённая покупка» для тех, у кого есть завершённый заказ. Это даёт баланс доверия и количества.
API для создания отзыва
public function store(Request $request, Product $product): JsonResponse
{
$request->validate([
'rating' => 'required|integer|between:1,5',
'body' => 'required|string|min:20|max:2000',
'title' => 'nullable|string|max:255',
'pros' => 'nullable|string|max:500',
'cons' => 'nullable|string|max:500',
'photos' => 'nullable|array|max:5',
'photos.*' => 'url',
]);
$isVerified = OrderItem::whereHas('order', fn($q) =>
$q->where('user_id', $request->user()->id)->where('status', 'completed')
)->where('product_id', $product->id)->exists();
$review = Review::create([
...$request->validated(),
'product_id' => $product->id,
'user_id' => $request->user()->id,
'is_verified_purchase' => $isVerified,
'status' => $this->needsModeration($request) ? 'pending' : 'approved',
]);
$product->recalculateRating();
return response()->json(new ReviewResource($review), 201);
}
Автоматическая модерация
class ReviewModerationService
{
private array $stopWords = ['http', 'www.', 't.me/', 'whatsapp'];
public function needsModeration(string $text, User $user): bool
{
foreach ($this->stopWords as $word) {
if (str_contains(strtolower($text), $word)) return true;
}
return $user->reviews()->where('status', 'approved')->count() === 0;
}
}
Автоматические правила: отзыв от верифицированного покупателя с рейтингом 4–5 и без стоп-слов → approved; первый отзыв нового пользователя → pending; текст с URL, телефоном или стоп-словами → pending или spam.
Пересчёт рейтинга товара
public function recalculateRating(): void
{
$stats = $this->reviews()
->where('status', 'approved')
->selectRaw('COUNT(*) as count, AVG(rating) as avg, SUM(CASE WHEN rating = 5 THEN 1 ELSE 0 END) as five_star')
->first();
$this->update([
'rating_avg' => round($stats->avg, 2),
'rating_count' => $stats->count,
]);
Cache::forget("product:{$this->id}:rating");
}
Рейтинг хранится денормализованно в таблице products для быстрой сортировки в каталоге. После каждого нового отзыва или удаления вызывается recalculateRating.
Как отзывы влияют на SEO?
Отзывы экспортируются в JSON-LD для Google. Звёзды в сниппете появляются при наличии минимум 1 отзыва. По нашим данным, это увеличивает CTR на 15–30%. Пример разметки:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": "{{ product.name }}",
"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": "{{ product.rating_avg }}",
"reviewCount": "{{ product.rating_count }}",
"bestRating": 5,
"worstRating": 1
},
"review": [
{% for review in product.topReviews %}
{
"@type": "Review",
"author": {
"@type": "Person",
"name": "{{ review.user_name }}"
},
"reviewRating": {
"@type": "Rating",
"ratingValue": "{{ review.rating }}"
},
"reviewBody": "{{ review.body }}",
"datePublished": "{{ review.created_at }}"
}
{% endfor %}
]
}
</script>
Сортировка и фильтрация отзывов
На странице товара отзывы сортируются по дате, рейтингу и полезности. Фильтр по звёздам — кликабельные строки в гистограмме. Это помогает покупателю найти релевантные отзывы и снижает bounce rate.
Ответы магазина
Менеджер может отвечать на отзывы из админ-панели с пометкой «Ответ магазина». Это повышает доверие и стимулирует пользователей оставлять больше отзывов.
Что входит в работу
- Документация API и описание эндпоинтов
- Обучение администраторов работе с модерацией
- Исходный код модуля отзывов (Laravel 11 + React 18)
- Бесплатная поддержка 30 дней после сдачи
- Гарантия на код 1 год
Процесс работы
- Аналитика — изучаем текущую архитектуру, нагрузку, требования к модерации.
- Проектирование — модель данных, API-контракты, схема кеширования.
- Реализация — бэкенд (Laravel 11, PostgreSQL), фронтенд (React 18, TypeScript).
- Тестирование — нагрузочное тестирование (до 1000 RPM), проверка модерации.
- Деплой — контейнеризация Docker, настройка CI/CD, мониторинг.
Сроки и стоимость
Базовая реализация занимает 4–7 рабочих дней. Сроки увеличиваются, если нужна интеграция с существующей CRM или нестандартные сценарии модерации. Стоимость рассчитывается индивидуально в зависимости от объёма работ. Свяжитесь с нами для консультации — мы рассчитаем точные сроки под ваш проект. Закажите разработку и получите систему отзывов, которая повысит конверсию и улучшит SEO.







