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

Bookmarks Notes Laravel Package

escolalms/bookmarks_notes

Laravel package that lets users bookmark content and attach personal notes to it. Ships the models and backend logic for creating bookmarks and notes, making it easy to add save-for-later and annotation features to any Laravel application.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require escolalms/bookmarks-notes

Verify the exact Composer name in the package's composer.json before requiring — EscolaLMS repos are often named with dashes (Bookmarks-Notes) while the Composer package uses underscores or vice versa. Also confirm the PHP/Laravel constraint matches your app; the last tagged release is Feb 2024, so a Laravel 11/12 app may need --ignore-platform-reqs or a fork.

Then:

php artisan vendor:publish --provider="EscolaLms\BookmarksNotes\Providers\BookmarksNotesServiceProvider"
php artisan migrate
php artisan route:list --path=bookmark

Read these first, in order:

  1. config/bookmarks_notes.php — table names, route prefix, middleware/guard, and (usually) the list of models that are allowed to be bookmarked/annotated.
  2. routes/ (or the route registration inside the service provider) — this is the real API contract; EscolaLMS packages register under /api/v1/... with the auth / Sanctum guard by default.
  3. src/Models + database/migrations — confirms whether it's polymorphic (bookmarkable_type/bookmarkable_id) or a fixed FK, and whether notes carry a free-text content or structured metadata.
  4. src/Contracts and src/Repositories — EscolaLMS binds interfaces in the provider; this is your extension seam.

First use case: a learner marks a course topic as a bookmark and attaches a private note. Exercise it end-to-end with php artisan tinker (resolve the repository contract, create for User::first()) before wiring the HTTP layer, then hit POST /api/v1/bookmarks with a Sanctum token. If that round-trips and GET returns only the caller's rows, you understand the package.

Implementation Patterns

Register a morph map before creating any data. If the package is polymorphic (it almost certainly is), stored *_type values are raw FQCNs by default. Add to AppServiceProvider::boot():

Relation::enforceMorphMap([
    'course' => \EscolaLms\Courses\Models\Course::class,
    'topic'  => \EscolaLms\TopicTypeGift\Models\TopicGift::class,
]);

Do this before the first bookmark exists, or write a data migration to rewrite existing rows. Skipping it means refactoring a model namespace later silently orphans every bookmark.

Resolve through contracts, don't new models. EscolaLMS packages bind Contracts\BookmarkContract / Contracts\NoteContract to repository implementations in the service provider. Inject those in your own controllers/listeners so you can decorate them:

public function __construct(private BookmarkContract $bookmarks) {}

public function index(Request $request)
{
    return BookmarkResource::collection(
        $this->bookmarks->forUser($request->user(), (int) $request->input('per_page', 15))
    );
}

Wrap creation in a domain service, not the controller. Bookmarking triggers events (see below), possibly notifications, and needs idempotency. A thin BookmarkService in your app that calls the contract + dispatches your own side effects keeps you decoupled from the package's HTTP layer, which you may want to bypass entirely and expose your own endpoints.

Hook the events for anything cross-cutting. Look for src/Events (e.g. BookmarkCreated, NoteCreated, NoteUpdated). Register listeners in your EventServiceProvider (or bootstrap/app.php on L11) to feed activity logs, analytics, or the EscolaLMS notifications package. Do not add logic by editing the package — listen.

Admin vs. user surface. EscolaLMS convention: user endpoints scope to auth()->user(), admin endpoints behind spatie/laravel-permission with GuardEnum::GUARD_API. If admin routes return 403 for your admin token, the permission seeder wasn't run — php artisan db:seed --class=BookmarksNotesPermissionSeeder (check database/seeders). Grant permissions to the admin role explicitly.

Pair with the content packages. The natural composition is Courses or TopicType* + this package: render a "Save" / "Add note" affordance per topic, then merge the bookmark state into the course tree response so the frontend doesn't need a second round-trip. Cache the per-user bookmark set keyed by user_id if you render it on every topic in a long course, and bust it on the *Created/*Deleted events.

Frontend consumption: stick to the package's own response shapes (JsonResource collections) and pagination params (per_page, page) — they match the rest of the EscolaLMS API and the official JS SDK. Don't reshape to your own envelope; you lose Swagger parity.

Gotchas and Tips

  • Uniqueness / double-taps. A user double-clicking "bookmark" will either violate a unique index or silently create duplicates depending on the migration. Use firstOrCreate semantics and catch QueryException on the composite key; if there's no unique index on (user_id, *_type, *_id), add one in your own migration.
  • Orphaned notes when content is deleted. Polymorphic relations don't cascade. Deleting a course leaves notes pointing at nothing. Add a deleting observer on annotatable models that purges via the contract, or a scheduled notes:prune command.
  • Authorization is not automatic. Verify that show/update/destroy on notes check ownership rather than just resolving by ID. In packages with little adoption this is the most likely real bug — write your own feature test asserting a 403 for cross-user access and keep it in your suite.
  • Route/middleware configuration. Guard and prefix usually come from config, but the provider may hardcode auth:api. Check before assuming a custom guard works; if it doesn't, override the route registration by disabling the package's routes (config flag or provider subclass) and re-declaring them in your routes/api.php.
  • Swagger. EscolaLMS packages annotate with @OA\. If the routes don't appear in your generated docs, the path isn't in config/l5-swagger.php annotations.paths — add vendor/escolalms/bookmarks-notes/src.
  • Maintenance risk. 0 stars, 0 dependents, last release Feb 2024, minimal activity. Treat it as a code sample you own: pin the version exactly, run the package's tests in your CI (vendor/bin/phpunit --filter BookmarksNotes), and be prepared to fork into app/ if a Laravel major bump breaks it. The migrations and models are small enough that inlining them is a realistic fallback — that's often cheaper than fighting a stale dependency.
  • Debugging checklist: php artisan route:list --path=bookmark → config('bookmarks_notes') → provider in bootstrap/providers.php → migration ran (Schema::hasTable) → morph map → permission seeder. Nine times out of ten a "not working" report lands on step 3 or 5.
  • Extension point for richer notes: if you need highlights (offset/length), tags, or visibility flags, don't patch the package — add your own note_meta table keyed to the note ID, or add a nullable json column via your own migration and cast it in a subclass. Keeps upstream diffs mergeable if a release ever lands.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource