Bookmarks System Implementation
Bookmarks allow users to save materials for later reading. Implemented as a polymorphic relationship — a single table for bookmarks of any entities (articles, products, vacancies).
Database
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: Bookmark Button
function BookmarkButton({ type, id, initialBookmarked }: BookmarkProps) {
const [bookmarked, setBookmarked] = useState(initialBookmarked);
const toggle = async () => {
setBookmarked(!bookmarked); // optimistic update
try {
await api.post(`/api/bookmarks/${type}/${id}/toggle`);
} catch {
setBookmarked(bookmarked); // rollback
}
};
return (
<button
onClick={toggle}
aria-label={bookmarked ? 'Remove from bookmarks' : 'Add to bookmarks'}
aria-pressed={bookmarked}
className={`bookmark-btn ${bookmarked ? 'bookmark-btn--active' : ''}`}
>
{bookmarked ? '🔖' : '🏷️'}
</button>
);
}
Implementation Timeline
Bookmarks system (polymorphic) with API and React UI: 1 day. With "My Bookmarks" page and type filtering: 1–2 days.







