merodiro/friendships
A Laravel package for adding friendships to Eloquent models. Send, accept, and deny friend requests, check relationship status, and organize friends into groups with a simple trait you drop into your User model.
Install the package with Composer:
composer install
php artisan vendor:publish --provider="...FriendshipServiceProvider" # if applicable
php artisan migrate
Add the package trait to the model that should be able to befriend others — typically App\Models\User:
use Vendor\Package\Traits\Friendable;
class User extends Authenticatable
{
use Friendable;
}
Look first at the trait’s public methods and the published config/migrations. The quickest first use case is sending and accepting a friend request:
$sender = User::find(1);
$recipient = User::find(2);
$sender->befriend($recipient);
$recipient->acceptFriendRequest($sender);
$sender->friends(); // collection of User models
$sender->friendsIds(); // array/collection of IDs
In v1.2.4, use friendsIds(). The older friends_ids() method has been renamed; update any existing calls before upgrading.
Check relationship state before acting. Guard UI and API actions with the package’s status helpers:
if (! $user->hasFriend($other)) {
$user->befriend($other);
}
if ($user->isFriendWith($other)) {
// show message thread, etc.
}
Use friend IDs for feed and notification queries. A common pattern is to fetch the current user’s friend IDs and feed them into a query:
$friendIds = $user->friendsIds();
$posts = Post::whereIn('user_id', $friendIds)
->latest()
->paginate();
This is especially useful for “friends only” visibility, direct-message eligibility, or shared-content filters.
Eager load friends when serialising profiles. Avoid lazy-loading friends inside loops:
$users = User::with('friends')->paginate();
return UserResource::collection($users);
In API resources, expose the new camelCase method consistently:
return [
'id' => $this->id,
'friends_count' => $this->friends()->count(),
'friend_ids' => $this->friendsIds(),
];
Separate request handling from relationship mutation. Wrap befriend(), acceptFriendRequest(), and unfriend() in dedicated controller actions or form requests rather than inline in larger update endpoints. This keeps authorization and notifications easier to reason about.
Authorise with policies. Friendship actions are user-to-user operations; use a policy or gate to ensure the authenticated user is allowed to act on the target user:
$this->authorize('befriend', $targetUser);
Invalidate cached friend lists after mutations. If you cache friendsIds() output, clear it on befriend, accept, reject, block, and unfriend paths. A model observer or event listener on the friendship actions is a clean place to do this.
Use the package’s events/listeners where available. If the package dispatches events such as friend request sent or accepted, hook notifications and audit logs there instead of duplicating logic in controllers.
v1.2.4 is a breaking rename. friends_ids() has changed to friendsIds(). Grep the codebase before upgrading:
grep -R "friends_ids" app resources routes tests
Update controllers, Blade templates, API resources, queued jobs, and test assertions. If you maintain a wrapper or service class around the package, update that too. Consider adding a test that calls friendsIds() so the upgrade path is covered.
Method names are camelCase. PHP method calls are case-sensitive in practice; friends_ids() will fail once removed. Do not assume a snake_case alias remains unless you verify the package source or changelog.
Watch for stale friend IDs after relationship changes. friendsIds() reflects the current persisted state. If you mutate friendship status and immediately reuse an earlier cached array, you may serve stale data. Re-query or invalidate caches after accept/unfriend operations.
Self-friending and duplicate requests. Validate that the target user is not the authenticated user and is not already a friend or pending request. The package may guard some cases, but your application should still return sensible validation errors.
N+1 queries when listing many users. Calling friends()->count() or friendsIds() per row in a table can produce many queries. Prefer aggregate queries, eager loading, or a dedicated “friends count” column/index if this is a hot path.
Testing. Use RefreshDatabase and create users in each friendship state you need: none, pending, accepted, blocked. Assert both the relationship helpers and friendsIds() output. If the package uses its own tables, run migrations in tests and avoid hard-coding table names.
Extension points. If the package allows a custom friendship model or configurable table names, set that in config before migrating. Prefer extending the trait or composing a service class rather than editing vendor code. If you need custom behaviour, override the trait methods in your User model carefully and call the parent implementation.
Debugging. When a friendship check returns unexpected results, inspect the underlying pivot/status records with toSql() or a query log, then verify the package’s expected status values. Most issues come from acting on the wrong side of the relationship or from assuming a pending request is already accepted.
How can I help you explore Laravel packages today?