Система закладок для сайту: polymorphic БД, Laravel API, React

Багато власників контентних сайтів стикаються з ситуацією: користувачі активно читають матеріали, але не можуть їх зберегти для перегляду пізніше. Відсутність системи закладок знижує повернення та час на сайті. При цьому проста реалізація часто призводить до дублікатів, повільних запитів і незручног

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 2062 послуг
Система закладок для сайту: polymorphic БД, Laravel API, React
Простий
~2-3 дні

Наші компетенції:

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1422
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1287
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    984
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1250
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    986
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Розробка веб-сайту для компанії ФІКСПЕР
    1000

Багато власників контентних сайтів стикаються з ситуацією: користувачі активно читають матеріали, але не можуть їх зберегти для перегляду пізніше. Відсутність системи закладок знижує повернення та час на сайті. При цьому проста реалізація часто призводить до дублікатів, повільних запитів і незручного UX. Ми впровадили десятки таких систем — від базового toggle до складних колекцій із синхронізацією між пристроями. Пропонуємо перевірене рішення на основі polymorphic зв'язку, яке підходить для будь-яких сутностей: статей, продуктів, вакансій, відео.

Як працює polymorphic зв'язок у базі даних?

Ключове завдання — спроєктувати базу даних так, щоб не плодити окремі таблиці під кожен тип контенту. Polymorphic зв'язок вирішує це однією універсальною таблицею bookmarks:

CREATE TABLE bookmarks ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, bookmarkable_id INTEGER NOT NULL, bookmarkable_type VARCHAR(50) NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE (user_id, bookmarkable_id, bookmarkable_type) ); CREATE INDEX ON bookmarks(user_id, bookmarkable_type, created_at DESC); 

Такий підхід гарантує унікальність закладки для кожного користувача та сутності, а індекс дозволяє швидко отримувати закладки користувача з сортуванням за часом. У реальному проєкті ми зіткнулися з кейсом, коли без індексу запит на 10000 закладок виконувався 2 секунди — після додавання індексу час впав до 10 мілісекунд. Документація Laravel: Polymorphic relations дозволяють моделі належати більш ніж одній іншій моделі через єдиний зв'язок.

Чому polymorphic зв'язок кращий за окремі таблиці?

Критерій Polymorphic Окремі таблиці
Кількість таблиць 1 N (за кількістю типів)
Складність запитів Один контролер N контролерів
Розширюваність Додати тип — один рядок Нова таблиця + код
Продуктивність Один індекс N індексів

Polymorphic підхід скорочує обсяг коду в 3–5 разів і спрощує підтримку. При появі нового типу контенту (наприклад, відео) достатньо додати запис у bookmarkable_type — без міграцій БД.

Покроковий план впровадження

  1. Аналіз і проєктування БД — 0.5 дня. Визначаємо типи сутностей, створюємо міграцію.
  2. Реалізація Laravel API — 0.5–1 день. Контролер toggle, валідація, документація.
  3. Створення React компонента — 0.5–1 день. Оптимістичний UI, доступність.
  4. Інтеграція та тестування — 0.5 дня. Юніт-тести, ручне тестування.
  5. Деплой і документація — 0.5 дня. Інструкція для розробника, деплой на сервер.

Як реалізовано toggle API на Laravel?

API побудований на одному ендпоінті, який перемикає стан закладки. Використовуємо контролер з методами toggle та index:

class BookmarkController extends Controller { public function toggle(Request $request, string $type, int $id): JsonResponse { $existing = Bookmark::where([ 'user_id' => auth()->id(), 'bookmarkable_type' => $type, 'bookmarkable_id' => $id, ])->first(); if ($existing) { $existing->delete(); return response()->json(['bookmarked' => false]); } Bookmark::create([ 'user_id' => auth()->id(), 'bookmarkable_type' => $type, 'bookmarkable_id' => $id, ]); return response()->json(['bookmarked' => true], 201); } public function index(Request $request): JsonResponse { $bookmarks = Bookmark::where('user_id', auth()->id()) ->when($request->type, fn($q) => $q->where('bookmarkable_type', $request->type)) ->with('bookmarkable') ->latest() ->paginate(20); return response()->json($bookmarks); } } 

Зверніть увагу: метод index підтримує фільтрацію за типом і пагінацію — це важливо для сторінки «Мої закладки». Без with('bookmarkable') ви отримаєте N+1 запитів, що неприйнятно при високому навантаженні.

Якщо вам потрібна така система — замовте реалізацію під ключ.

Приклад React компонента з оптимістичним UI
function BookmarkButton({ type, id, initialBookmarked }: BookmarkProps) { const [bookmarked, setBookmarked] = useState(initialBookmarked); const toggle = async () => { setBookmarked(!bookmarked); try { await api.post(`/api/bookmarks/${type}/${id}/toggle`); } catch { setBookmarked(bookmarked); } }; return ( <button onClick={toggle} aria-label={bookmarked ? 'Видалити із закладок' : 'Додати в закладки'} aria-pressed={bookmarked} className={`bookmark-btn ${bookmarked ? 'bookmark-btn--active' : ''}`} > {bookmarked ? '🔖' : '🏷️'} </button> ); } 

Оптимістичний UI: як уникнути затримок?

Оптимістичний update дає відгук за 0 мс, але вимагає обробки помилок. У проєкті для інтернет-магазину ми реалізували 5 кнопок закладок на сторінці — користувачі відзначали, що сайт став «літати» після впровадження такого підходу. Альтернатива — песимістичний UI з loader, але на мобільних пристроях він збільшує perceived latency на 300–500 мс. Використовуйте AbortController для запобігання гонці запитів при швидких кліках.

Процес роботи та терміни

Етап Тривалість
Аналіз і проєктування БД 0.5 дня
Розробка API (Laravel) 0.5–1 день
React-компонент та інтеграція 0.5–1 день
Сторінка «Мої закладки» з фільтрацією 0.5 дня
Тестування та деплой 0.5 дня

Разом: від 1 до 2 днів на базову реалізацію. Термін може збільшитися при необхідності синхронізації між пристроями або експорту закладок. В одному з проєктів знадобилося додати підтримку WebSocket для миттєвої синхронізації між вкладками — це зайняло додатковий день.

Що входить у роботу

  • Документація API (OpenAPI/Swagger)
  • Вихідний код з коментарями (Laravel + React)
  • Інструкція з розгортання (Docker + CI/CD)
  • Гарантія працездатності на обраному хостингу
  • 2 тижні підтримки після впровадження

Типові помилки при реалізації закладок

  • Відсутність унікального обмеження — користувач може додати одну сторінку кілька разів, що ламає логіку toggle.
  • Ігнорування оптимістичного UI — затримка 0.5–1 секунда вбиває відчуття відгуку.
  • N+1 запит при отриманні bookmarkable — обов'язково використовуйте with().
  • Неправильний вибір типу поля bookmarkable_type — використовуйте VARCHAR(50) з константами, щоб уникнути помилок.

Отримайте консультацію з впровадження закладок на ваш сайт — оцінимо ваш проєкт за один день і запропонуємо оптимальну архітектуру. Зв'яжіться з нами для розрахунку вартості проєкту. Наша команда має понад 5 років досвіду в розробці веб-додатків і виконала понад 50 проєктів із системами закладок та колекцій.