Клієнт приходить зі скаргою: каталог гальмує, фільтри завантажуються по хвилині, SEO-трафік падає через дублі сторінок. Найчастіше причина в архітектурі: неправильно вибрана модель категорій, неефективна пагінація або відсутність кешування. Розробка каталогу товарів — центральне завдання для інтернет-магазину, і її вирішення може заощадити до 40% бюджету на підтримку (зниження витрат на інфраструктуру — до 300 тис. грн на рік). За статистикою, 70% користувачів залишають сайт, якщо каталог завантажується довше 3 секунд, а правильно спроектований каталог може збільшити конверсію на 20%. Наш досвід — 8 років у розробці каталогів, понад 50 реалізованих проєктів для інтернет-магазинів з трафіком від 10 000 відвідувачів на день.
Ми розробляємо каталоги товарів з урахуванням сучасних практик і вашого бізнес-контексту. Наприклад, на проєкті з каталогом у 50 000 товарів ми скоротили час генерації сторінки з 6 с до 0.8 с за допомогою keyset pagination та кешування Redis.
Яку модель категорій обрати?
Дерево категорій зберігається в БД. Два поширені підходи: Adjacency List — кожен запис зберігає parent_id. Простота запису, але вибірка всього дерева вимагає рекурсивного CTE:
WITH RECURSIVE category_tree AS ( SELECT id, name, parent_id, 0 AS depth FROM categories WHERE parent_id IS NULL UNION ALL SELECT c.id, c.name, c.parent_id, ct.depth + 1 FROM categories c JOIN category_tree ct ON c.parent_id = ct.id ) SELECT * FROM category_tree ORDER BY depth, name; Nested Sets (MPTT) — кожен запис зберігає lft та rgt значення. Вибірка піддерева: WHERE lft BETWEEN :parent_lft AND :parent_rgt — один запит без рекурсії. Запис складніший: при додаванні вузла оновлюються всі праві сусіди. Пакет kalnoy/nestedset для Laravel.
Closure Table — окрема таблиця всіх пар предок-нащадок. Найгнучкіший, займає більше місця. Для складних операцій з деревом (переміщення піддерев) оптимальний.
| Підхід | Швидкість вибірки | Швидкість запису | Пам'ять |
|---|---|---|---|
| Adjacency List | Низька (рекурсія) | Висока | Мало |
| Nested Sets | Висока | Низька | Помірно |
| Closure Table | Висока | Середня | Багато |
Рекомендація: для каталогів до 10 000 категорій Adjacency List з кешуванням дерева в Redis — достатньо. MPTT — при частих вибірках піддерев без кеша.
Як правильно реалізувати атрибути товарів?
Товари різних категорій мають різні набори атрибутів. Три підходи:
- Таблиця з фіксованими колонками:
products.color,products.size,products.weight. Працює тільки при однорідному асортименті. Додавання нового атрибута — ALTER TABLE, міграція, деплой. - EAV (Entity-Attribute-Value): гнучко, але повільно при JOIN-ах. Для фільтрації за атрибутами потрібен Elasticsearch або денормалізований індекс.
- JSONB-колонка в PostgreSQL:
ALTER TABLE products ADD COLUMN attributes JSONB; CREATE INDEX ON products USING GIN (attributes); Компромісний варіант: гнучкість EAV, але без надлишкових JOIN-ів. Як зазначено в документації PostgreSQL, JSONB-колонки забезпечують гнучкість EAV без надлишкових JOIN-ів. Підходить для каталогів до 500 000 товарів.
Варіанти товару: Parent-Child
Товар з варіантами (колір × розмір) — поширене завдання. Два паттерни:
- Simple SKU: кожна комбінація — окремий запис у
products. Просто, але складно керувати батьківською карткою. - Parent-Child: батьківський товар типу 'variable' та дочірні 'variant' з конкретними комбінаціями атрибутів.
products (id, type, parent_id, sku, name, price, stock) -- type: 'simple' | 'variable' | 'variant' -- variant: parent_id → variable product При відображенні картки товару завантажуємо батька + всі його варіанти. Користувач обирає комбінацію атрибутів → знаходимо відповідний variant → оновлюємо ціну, фото, наявність. Для матриці варіантів використовуйте об'єкт, індексований за ID атрибута.
Структура URL та SEO
URL категорій — критично для SEO. Три варіанти:
- Плоский:
/catalog/noutbuki— просто, втрачає контекст ієрархії. - Ієрархічний:
/catalog/elektronika/kompyutery/noutbuki— краще для SEO, складніше при переміщенні категорії. - Гібридний:
/noutbuki-c142— читабельний slug + унікальний ID (стійкий до перейменувань).
Для фільтрованих сторінок: /noutbuki?brand=apple&ram=16 з canonical на /noutbuki або окремі SEO-сторінки для популярних комбінацій (/noutbuki-apple-16gb як статична сторінка-агрегатор). Schema.org: ItemList на сторінках категорій з ListItem для кожного товару в лістингу.
Пагінація та нескінченна прокрутка
Offset-пагінація: LIMIT 48 OFFSET 144. Працює, але при глибоких сторінках (OFFSET 10000) PostgreSQL все одно читає 10048 рядків. Рішення — keyset pagination:
SELECT * FROM products WHERE (sort_value, id) > (:last_sort_value, :last_id) ORDER BY sort_value, id LIMIT 48; Keyset pagination миттєва при будь-якій глибині, але не підтримує перехід на довільну сторінку.
| Тип пагінації | Продуктивність | Підтримка довільної сторінки | SEO |
|---|---|---|---|
| Offset | Падає на глибині | Так | Частково |
| Keyset | Висока | Ні | Краще (noindex) |
| Нескінченна прокрутка | Висока | Ні | Погано |
Для мобайлу — нескінченна прокрутка з IntersectionObserver, для десктопа з SEO-пріоритетом — класична пагінація (пошукові системи краще індексують сторінки з явними номерами).
Управління каталогом в CMS
Адміністративний інтерфейс каталогу:
- Bulk editing: виділити 50 товарів → змінити категорію/статус/ціну.
- Імпорт з CSV/XLSX: маппінг колонок, попередній перегляд з помилками, фонове завантаження через queue.
- Drag-and-drop сортування категорій: візуальне дерево з можливістю перетягування.
- Управління атрибутами: додати атрибут у категорію — він з'явиться на формах редагування всіх товарів категорії.
Для bulk-імпорту: Laravel Jobs + Horizon. Файл завантажується в S3, задача береться з черги, парситься рядково (через league/csv або PhpSpreadsheet), товари вставляються batch-ами по 100 записів.
Як прискорити каталог за допомогою кешування?
Сторінки каталогу — основне навантаження на БД. Стратегія кешування:
| Рівень | Що кешуємо | TTL |
|---|---|---|
| Redis | Дерево категорій | 1 година, скидання при зміні |
| Redis | Лістинг з фільтрами | 5–15 хвилин |
| CDN (Cloudflare) | HTML сторінок категорій | 5 хвилин, stale-while-revalidate |
| Браузер | Статика (зображення, JS, CSS) | immutable |
При зміні товару скидаємо кеш тільки тих сторінок, де він присутній. Cache tags в Laravel: Cache::tags(['category:electronics'])->flush(). Оптимальна конфігурація кешування може знизити час завантаження сторінки з 3 до 0.5 секунд. Кешування дозволяє скоротити витрати на сервери до 200 000 грн на рік.
Для каталогів з високою динамікою (часті зміни цін або залишків) варто скоротити TTL лістингу до 1-2 хвилин і використовувати invalidate за тегом. Для статичних каталогів — збільшити TTL до години.
Терміни
- Базовий каталог (категорії, список товарів, картка, пагінація): 2–3 тижні.
- З варіантами, EAV-атрибутами, імпортом та кешуванням: 4–7 тижнів.
- Інтеграція з Elasticsearch для пошуку та фільтрації додає 2–3 тижні.
Що входить в роботу
- Підготовка технічного завдання з детальною моделлю даних.
- Проектування та реалізація ієрархії категорій, атрибутів та варіантів.
- Розробка SEO-оптимізованої структури URL та Schema.org-розмітки.
- Налаштування пагінації та кешування.
- Інтеграція з адміністративною панеллю для управління асортиментом.
- Документування коду та API, навчання команди, передача доступів.
- Гарантійна підтримка протягом 30 днів після здачі.
Якщо хочете оцінити обсяг робіт для вашого проєкту, зв'яжіться з нами — ми проаналізуємо поточний каталог і запропонуємо рішення. Отримайте безкоштовний аудит вашого каталогу: залиште заявку на консультацію інженера. Наш фахівець зв'яжеться з вами протягом дня.
Типові помилки при проектуванні каталогу
- Використання offset-пагінації для каталогів >10 000 товарів — призводить до гальмувань на глибоких сторінках.
- EAV без індексації та кеша — вбиває продуктивність при фільтрації.
- Відсутність canonical на фільтрованих сторінках — плодить дублі та знижує ранжування.
- Плоский URL без захисту від перейменувань — втрачається SEO-вага.
Ці помилки збільшують час завантаження на 40% і знижують конверсію. Уникнути їх допоможе грамотне проектування з урахуванням сучасних практик.







