Laravel Packagev13.xMIT License0+ downloads

Bookmarks: Bookmarks that remember why they were saved

Add CanBookmark to the model that bookmarks and HasBookmarks to the model that is bookmarked, run one migration, and write $user->bookmark($post, ['note' => 'Read later']). Each bookmark carries a JSON object you read as $bookmark->meta->note, and a unique index keeps each pair to one row, even when two requests arrive at once.

$composer require pharaonic/laravel-bookmarks
Key Features

Bookmarks that carry their own data

One method writes them, native Eloquent relations read them.

JSON-Object Metadata

Store a note, a priority or a whole settings object on each bookmark, and read it back as $bookmark->meta->preferences->notify.

Replace or Keep, Never Merge

bookmark($post, $meta) replaces the whole object, bookmark($post) keeps it, and bookmark($post, []) empties it.

No Write When Nothing Changed

Metadata equal to what is stored, in any key order, runs no UPDATE, keeps updated_at, and fires no event.

Unique by the Database

A unique index keeps each bookmarker and bookmarkable pair to one row, and concurrent writes replace rather than merge.

Native Queries

whereBookmarkedBy($user), withBookmarkedBy($user), withCount('bookmarks') and JSON where('meta->priority', 5) run in SQL.

Any Model, Any Key

Users, teams or bots bookmark posts, products or each other, with integer, UUID and ULID keys in one table.

A quick look

A user saves a post with a note, and the feed shows what they saved.

routes/web.php
1use App\Models\Post;
2 
3Route::post('posts/{post}/bookmark', function (Post $post) {
4 $bookmark = request()->user()->bookmark($post, [
5 'note' => request('note'),
6 'remind' => request()->boolean('remind'),
7 ]);
8 
9 return ['note' => $bookmark->meta->note];
10})->middleware('auth');
11 
12Route::get('posts', function () {
13 return Post::withBookmarkedBy(request()->user())->latest()->paginate();
14});
FAQ

Bookmarks Questions

Quick answers about installing and using Bookmarks.

What is Bookmarks?

Let any Eloquent model bookmark any other, with JSON-object metadata on each bookmark: replace or keep it in one call, read it through an immutable value object, and rely on a unique index that keeps each pair to one row. It's a free, open-source Laravel package by Pharaonic.

How do I install Bookmarks?

Run composer require pharaonic/laravel-bookmarks in your project's root directory, then php artisan migrate.

What does Bookmarks require?

The latest release requires PHP 8.3+ and Laravel 13.

Does bookmarking again merge the metadata?

No. New metadata replaces the whole object, so a key you leave out is gone. Call bookmark() without metadata to keep what is stored, or pass [] to empty it.

What can the metadata hold?

A JSON object: strings, numbers, booleans, null, lists and nested objects. A list, a string or an object such as a Collection at the top is refused before anything is written.

What happens when the same user bookmarks twice at the same moment?

The table keeps one row for the pair. Both calls return that row, and the metadata ends up as one call's object, whole, never a mix of the two.

Is Bookmarks free to use?

Yes. Bookmarks is open source under the MIT license, so you can use it in personal and commercial projects.

Where can I find the Bookmarks documentation?

Read the Bookmarks documentation for setup, configuration, and usage examples.

How do I report a bug or contribute to Bookmarks?

Open an issue or a pull request on GitHub, or ask in the Pharaonic Discord.

Ready to dig in?

Read the full documentation for installation, configuration, bookmarking, metadata, relations, counts and filters, morph maps and key types, custom models, events, deletes and soft deletes, concurrency, the API reference, examples, and troubleshooting.