Model or dataset
overtrue/laravel-follow avatar
overtrue/laravel-follow

laravel-follow: a follow graph for Laravel with two traits and a pivot

:heart: This package helps you to add user based follow system to your model.

1,219 stars134 forksPHPMIT

At a glance

What is it?
overtrue/laravel-follow is an MIT licensed PHP package that gives a Laravel application a follow, unfollow and follow-request system through a Follower trait on the acting model and a Followable trait on the target, with relations, counts, ordering helpers and a status attachment helper. It ships as a Composer package with a service provider, migrations and configuration, and it belongs to a family of small social-graph packages.
Who is it for?
Adopt laravel-follow if your Laravel application needs users following users or following other models, and you want the follow request flow, the approved and pending relations and the counts included rather than assembling them by hand. Do not adopt it for a simple one-directional subscription list, since the package brings a service provider, migrations and a pivot table for relationships that are one query.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 19 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Two traits and a pivot table

laravel-follow is a package that helps you add a user based follow system to your model, and it is described as a user follow unfollow system for Laravel.

The whole design fits in two traits. The `Follower` trait goes on the model that acts, the user, and the `Followable` trait goes on the model that is followed, which is often the same `User` class but can be anything. The README's second example puts `Followable` on a `Channel` model, and that flexibility is the reason the package is not hardcoded around users.

php
use Overtrue\LaravelFollow\Traits\Follower;

class User extends Authenticatable
{
    use Follower;

    <...>
}

and on the target side:

php
use Overtrue\LaravelFollow\Traits\Followable;

class Channel extends Model
{
    use Followable;
    <...>
}

The implementation underneath is a pivot relation, so a follow is a row in a join table with a status. That is what makes the follow request feature possible at all: a follow is not a boolean, it is a record with an approval state, and a relation with an approval state needs a table rather than a flag on the user.

The repository layout is conventional for a Laravel package: `src/`, `config/`, `migrations/`, `tests/`, a `composer.json`, a `phpunit.xml.dist` and a `.php-cs-fixer.php` for coding style. There is also an `AGENTS.md`, which is the convention this project family uses for contributor guidance.

The package is MIT licensed, the primary language is PHP, and it belongs to a family of small social packages from the same author: laravel-like, laravel-favorite, laravel-subscribe and laravel-vote, listed as related projects in the README. If you need more than one of those behaviours, the family is worth a look, because they are designed to sit together.

Writing follows and reading them back

The write API is five methods on the follower side, and the read API is a small set of relations and predicates.

php
$user1 = User::find(1);
$user2 = User::find(2);

$user1->follow($user2);
$user1->unfollow($user2);
$user1->toggleFollow($user2);
$user1->acceptFollowRequestFrom($user2);
$user1->rejectFollowRequestFrom($user2);

$user1->isFollowing($user2);
$user2->isFollowedBy($user1);
$user2->hasRequestedToFollow($user1);

The symmetry of the predicates is worth noticing. `isFollowing` asks from the follower's side and `isFollowedBy` asks from the other direction, which matters when the same model carries both traits, because the two are not the same question and the package refuses to make you work out which one you wanted.

The relations come in three flavours each. There is `$user->followings`, `$user->approvedFollowings` and `$user->notApprovedFollowings`, and the same trio for `$user->followers`, `$user->approvedFollowers` and `$user->notApprovedFollowers`. The unfiltered relation is the one to be careful with, because it includes pending requests, and a naive `->followings()->count()` will over-report the number of people a user actually follows.

Because they are Eloquent relations, you can traverse them:

php
foreach($user->followings()->with('followable')->get() as $following)
{
    $following->created_at; // followed at
    
    $following->followable->nickname; // the user attributes
}

The `created_at` on the pivot row is the moment of the follow, which is the kind of field you need for a feed ordered by recency and the pivot gives it to you for free.

Follow requests, and the one method you have to remember to override

The follow request flow is the feature that distinguishes this package from a simpler follower counter, and it is implemented with a method you override.

If you would like to have some follow requests to need to be accepted by the user being followed, simply override the `needsToApproveFollowRequests()` method in the model that uses the `Followable` trait with your custom logic:

php
public function needsToApproveFollowRequests()
{
    // Your custom logic here
    return (bool) $this->private;
}

So the decision is per followable model and can depend on any attribute of that model. A private account demands approval, a public one does not, and a channel might have its own rule.

The consequence of this design is the one thing to check carefully. If you forget to override the method, the default behaviour applies, and the README's example of a default that returns whether the account is private is not a default the package installs for you. In other words, an account that should require approval will not, or an account that should not will, depending on the fallback. A forgotten override therefore does not throw an error, it silently changes who can follow whom, and that is a class of bug that is found in production rather than in tests.

Once requests exist, the follower side handles them with `acceptFollowRequestFrom` and `rejectFollowRequestFrom`, and the state shows up in the relations as `notApprovedFollowings` and `notApprovedFollowers` on the respective sides.

Counts, ordering and attaching the status to a collection

Three features handle the part of a follow system that every social feature ends up needing, the counts.

The first is aggregation through the relations, and they are ordinary Eloquent query builders, so conditions work as usual:

php
// followings count
$user->followings()->count();
$user->approvedFollowings()->count();
$user->notApprovedFollowings()->count();

// with query where
$user->followings()->where('gender', 'female')->count();

The second is the `*_count` attribute through `withCount`, which is Laravel's own mechanism and works with the package's relations:

php
$users = User::withCount(['followings', 'followables'])->get();
// or 
$users = User::withCount(['approvedFollowings', 'approvedFollowers'])->get();

The attribute names follow Laravel's snake case convention, so `approvedFollowings` becomes `approved_followings_count`. And because it is a query scope rather than an accessor, it can be used to filter and order in SQL, which is what you need for a leaderboard.

The third is ordering by follower count, which the package provides as three scopes: `orderByFollowersCountDesc()`, `orderByFollowersCountAsc()` and `orderByFollowersCount(string $direction = 'desc')`.

php
$users = User::orderByFollowersCountDesc()->get();
$mostPopularUser = User::orderByFollowersCountDesc()->first();

The separate helper is the interesting one, because attaching the follow status to a list of models is a problem the package solves directly. `Follower::attachFollowStatus(Collection $followables)` sets a `has_followed` attribute on each model, and it accepts a model, a `Collection`, a `Paginator`, a `LengthAwarePaginator` or an array. For a paginated list, the usage is:

php
$users = User::paginate(20);

$user->attachFollowStatus($users);

So a view can render a list of users with a follow button that knows the current state, without the view issuing a query per row.

The N+1 problem, and the two-query fix

The README has a section titled N+1 issue, and it is worth reading because it describes the single most common performance mistake in a follow feature.

The mistake is calling `isFollowing` or `isFollowedBy` inside a loop over a collection of models. Each call is a query, so a page of twenty users with a follow button on each one becomes twenty queries, and the cost grows with the page size.

The fix is eager loading, and the README is precise about which relationship to eager load for which call. To avoid the N+1 issue, you can use eager loading to reduce this operation to just 2 queries.

php
$users = User::with('followings')->get();

foreach($users as $user) {
    $user->isFollowing(2);
}

$users = User::with('followables')->get();

foreach($users as $user) {
    $user->isFollowedBy(2);
}

The asymmetry is the point. `isFollowing` checks whether the current user follows each of the listed users, which is answered by the current user's `followings` relation, so you load `followings`. `isFollowedBy` checks whether each listed user follows the current user, which is answered by the listed users' `followables` relation, so you load `followables`. Loading the wrong one leaves you with twenty queries and no error message.

This is a genuinely useful thing for the package to document, because the failure mode is a page that works in development with three users and crawls in production with three hundred.

The README also has an Events section, cut off in the version available here, which indicates the package emits model events for the follow lifecycle. The README links to a `CONTRIBUTING.md` for the contribution guidelines and a `composer.json` for the package definition.

Installing it and publishing what it needs

Installation is a Composer require and a vendor publish.

shell
composer require overtrue/laravel-follow -vvv

The `-vvv` flag is in the documented command, and it is worth keeping, because the package's service provider has to be discovered and the migrations have to be published, and the verbose output tells you whether the provider was registered.

Then the configuration and migrations are published:

shell
php artisan vendor:publish --provider="Overtrue\LaravelFollow\FollowServiceProvider"

That is the whole setup. The published artefacts are a configuration file and the migration that creates the follow pivot table, and after running it you have the migrations directory in your application and can run your normal migrate step.

The package is a service provider based Laravel package rather than something you call directly, so there is nothing to wire up in a kernel and no facade to import. The traits are the only API surface you touch, and the configuration file is where you would change table names if you need to.

One packaging detail to be aware of is the versioning. The project metadata records the default branch as `5.x`, while the recent releases include 6.0.0 from 2026-03-19, preceded by 5.3.0 from 2025-02-25 and 5.2.0 from 2024-03-13. The last push was on 2026-10-04 and the repository is not archived, so the project is alive, but the branch and release numbering are not aligned in a way that makes the branch name a reliable guide to what Composer will install. Check the release you are actually installing against your Laravel major version rather than assuming the branch tells you.

Where laravel-follow is the wrong tool

There are three cases where this package is more than you need.

The first is a one-directional subscription list. If a user subscribes to a newsletter and nothing else happens, a single boolean column and a scope is the whole feature, and a package with a pivot table, two traits, an approval state and a service provider is a schema decision rather than a convenience.

The second is a graph you need to query in ways this package does not model. The relations are one level, from a follower to the followable, and the pivot carries a status. If you need mutual follows to be a distinct state, or follow edges with weights or expiry, or multiple relationship types between the same pair of users, then you are past what the pivot table in this package represents and you want a graph edge model of your own. The package does have follow request approval, which is the common case, but it is approval and nothing more.

The third is a project on a Laravel version this release line does not support. The 6.0.0 release is a major bump, and a major bump in a package like this usually means a change to the trait contract or the relation names. The README's API examples are the 5.x shape, so if you are on the 6.0.0 tag, read its own documentation rather than assuming the README describes the code you installed.

The alternative approach, for comparison, is to write the pivot relation yourself. A `follows` table with a composite unique index on the two ids, a `status` column, and two belongsToMany relations is perhaps forty lines of code, and you own the schema, the indexes and the query plan. The difference in approach is that this package gives you a tested implementation, a follow request flow, counts and ordering helpers that would each be a few more hours of work, in exchange for a specific table shape and a naming convention. For a social feature that is more than a toggle, that is a good trade. For a column, it is not.

Editorial conclusion

Adopt laravel-follow if your Laravel application needs users following users or following other models, and you want the follow request flow, the approved and pending relations and the counts included rather than assembling them by hand. Do not adopt it for a simple one-directional subscription list, since the package brings a service provider, migrations and a pivot table for relationships that are one query. Verify three things first: that the version you install matches your Laravel major, since the package ships a 6.0.0 release while the default branch is 5.x and a mismatch there is easy to miss, that the published migrations use a table and column names your existing schema can accept, and that the `needsToApproveFollowRequests()` hook is overridden in every model that needs it, since the default behaviour is to approve nothing and a forgotten override shows up as follow requests that are never accepted.

Frequently asked questions

How do I add a follow system to a Laravel model?

Add the Follower trait to the model that acts and the Followable trait to the model that is followed, which can be the same User class or any other model. The README's examples show both traits on a User and the Followable trait on a Channel.

How do I install laravel-follow?

Run `composer require overtrue/laravel-follow -vvv`, then publish the configuration and migrations with `php artisan vendor:publish --provider="Overtrue\LaravelFollow\FollowServiceProvider"` and run your migrations.

How do follow requests work in laravel-follow?

You override the `needsToApproveFollowRequests()` method in the model that uses the Followable trait, with whatever logic you want, for example returning whether the account is private. The follower then uses `acceptFollowRequestFrom` and `rejectFollowRequestFrom`, and pending relations are exposed as `notApprovedFollowings` and `notApprovedFollowers`.

How do I count followers and followings in laravel-follow?

Through the relations, with `followings()->count()`, `approvedFollowings()->count()`, `notApprovedFollowings()->count()` and the same for followers, or through Laravel's `withCount`, for example `User::withCount(['approvedFollowings', 'approvedFollowers'])->get()`, which gives `approved_followings_count` and `approved_followers_count` attributes.

How do I avoid N+1 queries when checking follow status in laravel-follow?

Eager load the right relation before the loop. For `isFollowing` load `with('followings')` and for `isFollowedBy` load `with('followables')`, which the README says reduces the operation to just 2 queries. There are also `orderByFollowersCountDesc()`, `orderByFollowersCountAsc()` and `orderByFollowersCount($direction)` scopes.

Official sources

  1. Issues
  2. License: MIT
  3. overtrue/laravel-follow on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/overtrue-laravel-follow.svg)](https://hysenlabs.com/projects/overtrue-laravel-follow)

Was this useful?