Last Updated on February 4, 2019 by Amarjit Singh
Almost every social networking site has some sort of friendship system as that of Facebook. Where a user can send a friend request to other, accept or reject requests sent by other users, block other users. Now let’s start implementing these requirements step by step.
Created New Project:
Create a fresh Laravel project using this composer create project command.
composer create-project --prefer-dist laravel/laravel friendships
This will take some time to complete. Therefore sit back and let the composer download all the files. When the installation gets completed, run this command to enable login, registration and forgot password features in your project.
php artisan make:auth
Now we are ready to start creating a friendship system. First, we will create database tables required.
Creating Database tables required
We will create a table with name ‘friendships’. This table will be used to store friendship status between two users.
Create a migration with this schema.
//============== migration to create friendships table ====================== Schema::create(‘friendships’, function(Blueprint $table){ $table->increments(‘id’); $table->integer(‘first_user’)->index(); $table->integer(‘second_user’)->index(); $table->integer(‘acted_user’)->index(); $table->enum(‘status’, [‘pending’, ‘confirmed’, ‘blocked’]); $table->timestamps(); });
After defining above migration create friendships table with this command.
php artisan migrate
Now create an ORM Model for friendships table with this command.
php artisan make:model Friendship
Now we will have to implement a relation in User Model to fetch all of the friends. Since Eloquent doesn’t provide a relation that can fetch child records matching either of two columns. That is, the relations based on two columns are not provided by Eloquent. Therefore we will create two relations one will fetch friends based on first_user field and other will fetch friends based on the second_user field. Finally, we will merge friends fetched by these two relations to get all friends. Therefore in your User model paste the following code.
//======================== functions to get friends attribute ========================= // friendship that this user started protected function friendsOfThisUser() { return $this->belongsToMany(User::class, ‘friendships’, ‘first_user’, ‘second_user’) ->withPivot(‘status’) ->wherePivot(‘status’, ‘confirmed’); } // friendship that this user was asked for protected function thisUserFriendOf() { return $this->belongsToMany(User::class, ‘friendships’, ‘second_user’, ‘first_user’) ->withPivot(‘status’) ->wherePivot(‘status’, ‘confirmed’); } // accessor allowing you call $user->friends public function getFriendsAttribute() { if ( ! array_key_exists(‘friends’, $this->relations)) $this->loadFriends(); return $this->getRelation(‘friends’); } protected function loadFriends() { if ( ! array_key_exists(‘friends’, $this->relations)) { $friends = $this->mergeFriends(); $this->setRelation(‘friends’, $friends); } } protected function mergeFriends() { if($temp = $this->friendsOfThisUser) return $temp->merge($this->thisUserFriendOf); else return $this->thisUserFriendOf; } //======================== end functions to get friends attribute =========================
In the above code, the function friendsOfThisUser() will fetch the friends that are invited by your friend request and the function thisUserFriendOf will fetch the friends that sent you the friend request.
Now in order to access all friends by simply calling $user->friends
. we have implemented getFriendsAttribute() function. This function simply checks if there exists is any friends key in relations array. If it finds that then it returns a collection of friends by simply calling $this->getRelation('friends')
. But if it doesn’t exist then it first calls loadFriends() function which sets the friends relation and then it calls $this->getRelation('friends')
and returns a collection of friends.
The loadFriends() funtion further calls mergeFriends() function which merges the collection returned by the relation friendsOfThisUser, with the collection returned by relation thisUserFriendOf.
In this way, we are able to get a collection of all friends of a user by simply calling $user_model->friends
In a similar manner, we can use above technique to get blocked_friends by using following code.
//====================== functions to get blocked_friends attribute ============================ // friendship that this user started but now blocked protected function friendsOfThisUserBlocked() { return $this->belongsToMany(User::class, ‘friendships’, ‘first_user’, ‘second_user’) ->withPivot(‘status’, ‘acted_user’) ->wherePivot(‘status’, ‘blocked’) ->wherePivot(‘acted_user’, ‘first_user’); } // friendship that this user was asked for but now blocked protected function thisUserFriendOfBlocked() { return $this->belongsToMany(User::class, ‘friendships’, ‘second_user’, ‘first_user’) ->withPivot(‘status’, ‘acted_user’) ->wherePivot(‘status’, ‘blocked’) ->wherePivot(‘acted_user’, ‘second_user’); } // accessor allowing you call $user->blocked_friends public function getBlockedFriendsAttribute() { if ( ! array_key_exists(‘blocked_friends’, $this->relations)) $this->loadBlockedFriends(); return $this->getRelation(‘blocked_friends’); } protected function loadBlockedFriends() { if ( ! array_key_exists(‘blocked_friends’, $this->relations)) { $friends = $this->mergeBlockedFriends(); $this->setRelation(‘blocked_friends’, $friends); } } protected function mergeBlockedFriends() { if($temp = $this->friendsOfThisUserBlocked) return $temp->merge($this->thisUserFriendOfBlocked); else return $this->thisUserFriendOfBlocked; } // ======================================= end functions to get block_friends attribute =========
Now to get all friend requests sent to you we can use following Eloquent relation.
public function friend_requests() { return $this->hasMany(Friendship::class, ‘second_user’) ->where(‘status’, ‘pending’); }
Using the Friendship System
Sending friend request: To send a friend request we will create an object of Friendship Model and set the value of the first_user field to the id of the user who is sending the friend request and value of second_user will be the id of the user to whom the friend request is being sent. Also, set the value of the acted_user to the id of the user who is sending the friend request. Finally set the value of the status field to ‘pending’.
Accepting a friend request: To accept a friend request simply change the acted_user field value to the id of the user who is accepting the request and also change the status field to ‘confirmed’.
Rejecting a friend request: To reject the friend request simply delete the friend request.
Blocking a user: While blocking a user there will be two cases.
- the user is already a friend of you: In this case, simply change the value of acted_user field to the id of the user who is blocking the other and also change the value of the status field to ‘blocked’.
- the user is not a friend of you: In this case, we will follow the steps to send a friend request with an exception that the value of the status field will be set to ‘blocked’.
Thank you, is the simplest code i see about friendship, i am trying to implemen in my app, i will use to get friendship via API, but i need a little more info about the implementation, do you have some project finished or some videos?
Glad to know that you found it helpful. Yes, I have a project, in fact, this code is from that project. But I don’t have any video tutorial related to this code. All the knowledge to implement the friendship system is gathered from Stack Overflow. If you can more precisely elaborate your problem then I will help you find a perfect solution.
Ok, I will consider making a post for frontend part. Thanks for the suggestion.
In your controller method that is going to handle the request to block a user you will simply do all the steps of sending a friend request with an exception that the value of status field will be “blocked” instead of “pending”.
Every time when we access the “friends” property from the user model the Laravel executes an SQL query. So we added the check `(!array_key_exists(‘friends’, $this->relations)) ` because we don’t want to execute the query more than one time in same request-response cycle. This will help to reduce the request time.
How do you count how many friends each user has?
Do this
$user->friendsOfThisUser()->count() + $user->thisUserFriendOf()->count()
How to display a list of all users and show at what stage a friend request for an authorized user, in order to display a button on the frontend, for example, “Add as friend”, “Accept application”, “Remove from friends”, “Block”? And is this done through a resource?
Well in Laravel you can get all the users using
User::all()
and to show the friendship status you can do something as follows:
// In User Model add this
public function getFriendship(User $user) {
return Friendship::where(function($q) use($user){
$q->where(function($q) use($user) {
$q->where('first_user', $user->id)
$q->where('second_user', $this->id);
})->orWhere(function($q) use($user) {
$q->where('first_user', $this->id)
$q->where('second_user', $user->id);
});
})->first();
}
// Then in your blade do this
@php
$friendship = $user->getFriendship(auth()->user());
@endphp
@switch($friendship->status)
@case('pending')
@if ($frienship->acted_user == $user->id)
<button> Accept friend request</button>
@else
<span> Friend request sent</span>
@endif
@endcase
@case('confirmed')
<button>Un-friend</button>
<button>Block</button>
@endcase
@case('blocked')
@if ($frienship->acted_user == $user->id)
<span> This user has blocked you</span>
@else
<button> Un-block</button>
@endif
@endcase
@endswitch
not working
Please elaborate your issue.