Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Bookmarkable Laravel Package

x-laravel/bookmarkable

x-laravel/bookmarkable adds bookmarking to Laravel Eloquent models with a simple trait. Let users save favorites or bookmarks on any model through polymorphic relations, with straightforward attach, detach, and toggle methods plus convenient per-user queries.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit The package is a domain-layer Eloquent component: two contracts/traits pairs (CanBookmark + Bookmarker for the owner, HasBookmarks + Bookmarkable for the saved model) implementing the standard polymorphic many-to-many pattern already familiar from favourites/tagging packages. Splitting the two sides is the right call — it lets us attach saving to User/Team without touching content models, and vice versa. Folders with ULIDs, a position + last-activity ordering, and a configurable default folder give us a ready-made data model for a "saved items" feature rather than a bare pivot. Config-driven connection, tables and model classes mean we can extend or relocate without forking. It ships no UI, no routes and no controllers, which is appropriate for a package but means the product surface is entirely ours to build.

Integration feasibility Low-to-moderate effort for a greenfield feature. The main friction points are: (a) composer constraints are not stated in the release notes — Laravel and PHP minimums must be confirmed against our matrix before we commit; (b) migration collisions if any existing favourites/bookmarks table exists; (c) withBookmarkedBy() is only a win if the supporting indexes ship with the migrations, which is unspecified; (d) the "shared" folder flag has no accompanying membership or permission model, so sharing as a feature is not actually delivered by v1.0.0.

Technical risk

  • v1.0.0 with no track record. Pre-1.x semver on a first release means minor bumps can break; pin exactly.
  • Shared folders are a semantic gap, not a feature. A private|shared flag without a membership pivot or gate hook invites an authorization bug that becomes a data-exposure incident. Highest severity item in this release.
  • Prune correctness. Orphan pruning across a configurable connection/table set is powerful but under-specified (what counts as an orphan, behaviour when FKs are absent, safety with soft deletes and morph-map changes).
  • Events after commit. Good default, but listeners that throw leave the bookmark persisted with side effects missing; listeners must be idempotent.
  • Ordering is read-only in this release. ordered() exists but no reorder/move API is documented, so position writes may fall to application code.
  • Uniqueness/idempotency of bookmark() is unstated — duplicate rows would corrupt counts and saved-state queries.
  • Hard-coded default-folder constraints (cannot rename, share or delete) are a product limitation that will surface in support tickets and in GDPR deletion flows.

Key questions

  1. Laravel/PHP version matrix and illuminate/* constraints? Test coverage and CI matrix?
  2. Does shared come with a membership pivot, policy/gate hook, or invitation flow — or is enforcement left entirely to us?
  3. Is there a reorder/move API for position, or do we write it ourselves?
  4. Are unique constraints on (bookmarker, bookmarkable, folder) present? Is bookmark() idempotent?
  5. Which indexes ship with the migrations, and does withBookmarkedBy() avoid N+1 for folder listings and counts?
  6. What exactly does model:prune delete, and is it safe when FKs are disabled or morph aliases change?
  7. How do soft deletes interact with cascade cleanup and pruning?
  8. Can we add columns (e.g. tenant_id) to the shipped models via config, or does that require forking?
  9. Multi-tenancy: is any global scoping provided, or fully delegated to the app?
  10. Are tr/en strings publishable language files, and can we add locales without editing vendor code?
  11. License, security disclosure policy, maintainer count, release cadence.

Integration Approach

Stack fit Standard Laravel + Eloquent application, API-only or Blade, MySQL 8/Postgres/SQLite. No JS or queue assumptions. Requires a running scheduler for pruning and working after_commit semantics for events. Fits cleanly alongside existing traits on User and content models; watch for trait method-name collisions (bookmark, hasBookmarked are generic enough to clash with in-house helpers).

Migration path No predecessor package, so this is greenfield. If we already have a favourites table, treat it as a data migration, not a code migration: map legacy rows into the new bookmarks table (and into each user's default folder) rather than adapting the package schema. Publish config and migrations, place the tables on the intended connection up front — moving a high-volume table later is a coordinated deploy.

Compatibility Verify: model:prune requires the package models to implement Prunable; ULID folders need a char(26) column and index-length room on MySQL; morph aliases must be registered before backfill or rows orphan; transaction wrapping in our HTTP layer determines whether after-commit events fire; DB engine parity for ordered() queries (window functions vs. order-by).

Sequencing

  1. Spike (timeboxed): install in a branch, run our CI matrix, execute migrations against our production engine, read the source for the shared-folder and prune behaviour.
  2. Phase 1 — default folder only. Single "save/unsave" toggle per user, no folders in UI. Lowest risk, delivers most of the user value.
  3. Phase 2 — user folders. Create/list/rename/delete plus ordering once the reorder API question is answered; scope decisions for tenancy here.
  4. Phase 3 — pruning in production with monitoring and a conservative chunk size, after verifying scheduler health.
  5. Phase 4 — shared folders, gated on a written authorization design (policy, membership model, audit) that the package does not currently provide.

Operational Impact

Maintenance Small dependency surface, but a single-maintainer, pre-1.x package: pin the exact version, subscribe to releases, read changelogs before every bump. Insurance cost is low — the code is a handful of traits, contracts, models and one command, so a fork is a day's work rather than a re-platforming.

Support No vendor SLA. Documentation depth is unknown; Turkish/English messages suggest an originating team whose working hours may not overlap ours, so treat source as the primary reference and budget for internal ownership. Name an internal owner for the package before adoption.

Scaling Bookmarks will be among the highest-row-count tables in the product (users × items). Insist on composite indexes for (bookmarker, folder) and (bookmarkable_type, bookmarkable_id) before launch. withBookmarkedBy() is one query per list page — good — but "all saved items" and folder listings need pagination we implement ourselves. Pruning cost scales with orphan volume; chunk size must be tuned to avoid lock storms. The separate-connection option is attractive for read scaling but breaks cross-database joins with the main app — treat as a last resort.

Failure modes

  • Scheduler not running (common in containerised deploys) → orphan rows accumulate silently; alert on prune job execution, not just failure.
  • Shared-folder leakage if the flag is honoured without enforcement → cross-user data exposure; the single most damaging scenario here.
  • Partial side effects when after-commit listeners fail; design listeners to be idempotent and replayable.
  • Prune misconfiguration (oversized chunks, wrong connection) → long locks or deletion of the wrong rows; run first in a dry-run/staging pass.
  • Soft-deleted owners/items never triggering cleanup, leaving inflated counts and stale saved-state.
  • Morph alias changes orphaning rows that pruning then deletes unexpectedly.
  • Default-folder immutability colliding with account deletion and data-export flows — verify cascade fires on hard delete.

Ramp-up One backend engineer: ~1–2 days to wire the default-folder save/unsave flow with tests, +2–3 days for folder CRUD and ordering in the API, +1 day for scheduler/prune plus monitoring, and unbounded time for shared folders until authorization is designed. Product must supply all UI (saved-state toggles, empty states, folder management). Team should be comfortable with polymorphic relations, morph maps, model:prune and after-commit events.

Recommendation: adopt, pinned, behind a Phase 1 default-folder-only scope; treat folders as a Phase 2 deliverable and shared folders as unshipped until the authorization question is answered in writing.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource