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 Followers Laravel Package

lecturize/laravel-followers

Add follow/unfollow relationships to any Eloquent model in Laravel. Traits let models follow and be followed polymorphically, with helpers like follow(), unfollow(), toggleFollow(), isFollowing(), plus followers() and following() relations and query scopes.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is a small polymorphic pivot layer: a followers table plus two traits, one for the model doing the following and one for the model being followed. There is no feed generation, no notification plumbing, and no UI — it is deliberately a persistence primitive, so budget time for the application-level pieces.

Minimal setup

composer require lecturize/laravel-followers
php artisan vendor:publish --provider="Lecturize\Followers\FollowersServiceProvider"
php artisan migrate

If the provider tag differs in your version, run php artisan vendor:publish and pick the lecturize/followers entries; you want both the config file and the migration. Read the published migration before running it — that file is the real contract of the package and tells you the exact column names (follower_id/follower_type/followable_id/followable_type, plus an accepted flag in recent versions), indexes, and whether a unique constraint exists.

Wire up the models. Put both traits on the models that can follow and be followed (typically User); put only Followable on things that are follow targets only (a Team, Category, or Author profile).

use Lecturize\Followers\Traits\Follower;
use Lecturize\Followers\Traits\Followable;

class User extends Authenticatable
{
    use Follower;
    use Followable;
}

First use case — a "Follow" button and a profile counter:

$author->follow($user);              // or $user->follow($author)
$author->isFollowedBy($user);        // true
$author->followers()->count();
$user->followings;                   // collection of followed models

If those method names don't resolve, check the trait source in vendor/lecturize/laravel-followers/src/Traits — it is a few hundred lines and reading it once will save you an afternoon.

Implementation Patterns

Idempotent follow/unfollow endpoints. Wrap the trait calls in a small service rather than calling them straight from controllers, so you can enforce self-follow guards and emit domain events in one place:

class FollowService
{
    public function follow(User $actor, Model $target): void
    {
        abort_if($actor->is($target), 422, 'You cannot follow yourself.');

        if (! $actor->isFollowing($target)) {
            $actor->follow($target);
            FollowCreated::dispatch($actor, $target);
        }
    }
}

Note that toggleFollow() is convenient for a UI button but is a poor fit for an API — retries and double-clicks flip state. Prefer explicit follow/unfollow endpoints with an idempotent check.

Follow requests for private accounts. The accepted column (when present) lets you model pending follows: create the row unaccepted, expose pendingFollowRequests() on the target, and only count accepted rows in follower counts. Treat this as a state machine in your own code — the package gives you the column, not the workflow or the authorization.

Polymorphic targets. Because both sides are morphs, a user can follow other users, teams, and topics from the same table:

$user->follow($team);
$user->follow($category);

$user->followings()->with('followable')->get()->groupBy('followable_type');

This is the package's strongest feature and its main trap: never assume $user->followings is a list of User models. Group by type before hydrating, or add typed accessors (followedUsers(), followedTeams()) on your model that filter on the morph type.

Feed queries. Build the timeline yourself from the followed IDs, grouped by type, and paginate the underlying query rather than the relation:

$followedIds = $user->followings()
    ->where('followable_type', (new User)->getMorphClass())
    ->pluck('followable_id');

$posts = Post::whereIn('user_id', $followedIds)
    ->latest()
    ->paginate(20);

Counts and eager loading. Use withCount where possible instead of calling followers()->count() in a loop; a profile index page with 30 users will otherwise issue 60 queries.

User::withCount(['followers', 'followings'])->paginate();

Authorization. Put the "can this user follow that model" decision in a policy and call it from both the controller and any Blade component that renders the button, so the UI and the API cannot drift. The package performs no permission checks.

Notifications. Notifications on follow require a wrapper. In the service above, FollowCreated can be a plain Laravel event; a queued listener sends the mail/database notification. Do not expect the package to fire events.

Gotchas and Tips

  • Maintenance risk is the headline. 12 stars and a last release of February 2023 means you are adopting an unmaintained-ish dependency. Pin it in composer.json, check its constraint against your Laravel version (Laravel 11/12 support is not guaranteed), and accept that a bug fix may mean forking. The upside: the package is small enough to vendor or copy the two traits and migration into app/ if the constraint blocks an upgrade.
  • Morph type values are fully-qualified class names unless you register a morph map. If you later move App\Models\User to App\Domain\User, every existing row silently stops matching. Register Relation::enforceMorphMap([...]) early and never rename casually.
  • Duplicate rows. Check whether the published migration has a unique composite index on (follower_id, follower_type, followable_id, followable_type). If it does not, follow() called twice will create two rows and inflate counts — guard in the service and add the index in your own migration.
  • Self-follow. Nothing stops a model following itself. Add the check explicitly; it will otherwise show up in follower counts and feed queries.
  • Orphaned rows. There is almost certainly no cascading cleanup when a user is deleted, especially on the morph side. Add a deleting observer that removes rows where the model is the follower or followable, or schedule a pruning command.
  • accepted semantics. Confirm the default in the migration. If it defaults to true, every existing query that "just counts followers" will include pending requests the moment you enable private accounts. Write counts as where('accepted', true) from day one so behaviour does not change later.
  • Index length on MySQL. Four columns with string morph types plus a unique index can exceed key-length limits on older MySQL with utf8mb4. If the migration fails, shorten the *_type columns to 191 or index only the IDs.
  • Config vs migration drift. If you change the table name in the config after migrating, the traits will query a table that does not exist. Publish, edit, then migrate — in that order — and re-run config:clear.
  • Testing. Use RefreshDatabase and assert against the pivot directly: assertDatabaseHas('followers', ['follower_id' => $a->id, 'followable_type' => $a->getMorphClass()]). Testing via counts alone hides duplicate-row bugs.
  • Extension points are limited to the migration and your own wrapper. There is no built-in event, observer, or custom pivot model hook. Treat the traits as repository methods: call them only from a FollowService so that swapping the package out (or forking it) touches one class instead of every controller.
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.
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
andydefer/laravel-notification
adachsoft/cli-process-tester
jhaskell13/laraflow
sebastienheyd/boilerplate-media-manager
ccast/tagixo-primix
pretendtrue/laravel-builder
sawmainek/laravel-scout-tntsearch-driver
tomshaw/laravel-calendar-table
dibakar/laravel-ownership
steadrun/laravel-monitor
labyrinthe/connect-laravel
elmogy/layan
drewdan/sentinel-client
dskripchenko/laravel-llm
fatryst/nuonuo