Реализация системы закладок
Закладки позволяют пользователям сохранять материалы для последующего чтения. Реализуется как polymorphic связь — одна таблица для закладок любых сущностей (статьи, продукты, вакансии).
База данных
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);
Laravel: API
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);
}
}
React: кнопка закладки
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>
);
}
Срок реализации
Система закладок (polymorphic) с API и React UI: 1 день. С страницей «Мои закладки» и фильтрацией по типу: 1–2 дня.







