Клієнт прийшов зі скаргою: сторінка товару завантажується 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.







