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

Laravel Subscribe Laravel Package

overtrue/laravel-subscribe

A lightweight Laravel package that adds subscribe/unsubscribe behavior to Eloquent models via traits. Users can follow posts or any model with subscribe, unsubscribe, toggle and isSubscribed helpers, plus subscribers and subscriptions relations, a publishable migration and events.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Check your platform before touching the version constraint. 5.0.0 requires PHP 8.3+ and Laravel 13.x. If the app runs Laravel 11/12 or PHP 8.1/8.2, pin "overtrue/laravel-subscribe": "^4.0" and stop reading. If you are on Laravel 13, composer require overtrue/laravel-subscribe:^5.0.
  • Read UPGRADING.md first. The release notes explicitly point there. The good news: the public API, events, config and database schema are unchanged, and no migration republishing is required — so an upgrade is a Composer bump plus whatever UPGRADING.md lists, not a data migration.
  • Look first at: the published config/subscribe.php (polymorphic ID/UUID settings), the package migration (the subscriptions table with its unique index), and the two traits — Subscriber (the actor, usually User) and Subscribable (the thing being subscribed to, e.g. Post, Channel).
  • First use case: add Subscriber to User and Subscribable to a target model, then $user->subscribe($post) / $user->unsubscribe($post) / $user->toggleSubscribe($post), and read state with $user->isSubscribed($post) or $post->isSubscribedBy($user). That covers the 80% case in about ten lines.
  • Publish config/migrations with the package's vendor:publish tags. 5.0 added explicit coverage for configuration and migration publishing, so if publishing previously felt flaky in your setup, re-verify the tags you use in CI/provisioning scripts.

Implementation Patterns

Feature toggles read better than raw subscribe/unsubscribe. A single controller endpoint backed by toggleSubscribe() plus isSubscribed() for the render keeps the UI honest and avoids double-submit rows racing the unique index:

$user->toggleSubscribe($post);
return back()->with('subscribed', $user->isSubscribed($post));

Guard with authorization, not just existence. The package does not know your policy layer. Wrap mutations in $this->authorize('subscribe', $post) — otherwise subscribe() happily creates rows for targets the user should never see. Combine with route-model binding for the target.

Attach status to lists instead of looping. The 5.0 notes call out expanded coverage for attaching subscription status across models, collections and pagination inputs. When rendering an index, use the collection/paginator attachment path the package exposes rather than calling isSubscribed() per row — that is the difference between one query and N+1 on a page of 25. If you already have a hand-rolled loop, this is the single highest-value refactor after upgrading.

Prefer eager loading for counts and lists. $user->subscriptions and $post->subscribers are morph relations; withCount('subscribers') / load('subscriptions') on the index query keeps the same semantics without the per-row hit. Cache aggregate counts (subscriber totals on a channel page) in your own layer if the page is hot — the package won't do it for you.

React to events, don't fork the package. Subscribed / Unsubscribed events are part of the preserved API. Put notifications, feed fan-out, analytics and cache invalidation in listeners. This keeps your upgrade path to 6.x clean and is exactly what the "events preserved" guarantee in 5.0 is protecting.

Polymorphic IDs. With UUID/ULID primary keys on either side, configure the package's ID setting and keep the morph columns consistent with that type. Use a morph map (Relation::enforceMorphMap([...])) so stored subscribable_type values are stable aliases, not FQCNs — 5.0 expands polymorphic ID coverage, but it cannot retroactively fix type strings you already wrote.

Testing. Event::fake([Subscribed::class]) to assert intent without side effects, RefreshDatabase for the pivot table, and assert idempotency: subscribing twice should not produce two rows. Use assertDatabaseHas on subscriptions for the happy path and a policy test for the denial path.

Gotchas and Tips

  • The 4.x → 5.0 cliff is the framework, not the package. Most failed upgrades will be Laravel 13 / PHP 8.3 requirements surfacing as Composer resolution errors, not runtime breaks. Resolve the platform first; the subscription code itself should be untouched.
  • Do not republish migrations. 5.0 states the schema is preserved and no republishing is required. Re-running vendor:publish over an existing app and then migrate is how you get a duplicate-table or a silently divergent schema. Publish on fresh installs only.
  • Re-publish config only deliberately. Laravel's config cache plus a changed package default is a classic "why is this still behaving like the old version" bug. Diff the published config against the package copy after upgrading, then php artisan config:clear.
  • The unique index is your friend and your 500. Concurrent or double-clicked subscribe requests can throw a query exception rather than being a no-op. Either catch it and treat as success (the desired state was reached) or wrap the toggle in a transaction with a row lock.
  • Morph map drift is a silent data bug. Renaming a model class or introducing enforceMorphMap after rows exist leaves orphaned subscribable_type values that simply stop matching — lists go quietly empty instead of erroring. Decide the morph strategy before the first production subscription and migrate type values in one explicit pass if you change it.
  • Event listeners fire inside the request. Slow listeners (mail, HTTP fan-out) will be felt directly in the toggle endpoint. Push them to a queue, and be aware that listeners registered with ShouldHandleEventsAfterCommit behave differently inside a wrapping transaction — relevant if you also do the lock-based toggle above.
  • Extension points, in order of safety: events → your own service class wrapping the traits → forking. Don't override the package's models unless you must; every override is upgrade debt against a package that just shipped a clean, schema-compatible major.
  • On PHP 8.5 / Laravel 13, this release is CI-validated (20 tests, 106 assertions across 8.3–8.5 plus lowest-dependency runs), so treat a green Composer install with no code changes as the expected outcome — if you are changing code during the upgrade, question why.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core