I have already done a Facebook Style Relationship System in a previous post that covers the database design and PHP implementation of various friendship actions like Sending a friend request, Accepting Friend request, Blocking a user etc. This post covers another very usefull feature that was requested by users, Friends Suggestions for the currently logged in user.
Read the post on Social Network Friends Relationship System before continuing with this post, since it introduces the Relation
class and the base elements that will be used here.
In the demo, view the friend suggestion panel for the suggested friends to be loaded.
Database Schema
Database contains 2 tables, users table which ofcourse contains the users details and the relationship table which contains the friends connection details.
Database Schema
Directory Structure
The app/ directory contains the important Class’es that are used for in the system. includes/ is where the small snippets of php scripts are kept.
- Friends Relation System/
- app/
- Relation.php
- Relationship.php
- User.php
- config.php
- ajax/
- get_friend_suggestions.php
- js/
- app.js
- includes/
- blocked_friends.php
- blocked_profile.php
- sent_friend_requests.php
- user_friend_requests.php
- user_friends.php
- login.php
- home.php
- check_login.php
- logout.php
- profile.php
- user_action.php
- app/
Friend Suggestion Design
To get the suggested friends for a user let us assume the relationship as a graph structure. Let each Node in a graph represent a User and each Edge(connections) in the graph represent the relationship between them. If two users are connected then they are Friends.
Following is the graph representing a sample users data.
Friends relationship Graph
Assuming that user 1 is logged in, let’s look into how we could fetch the suggested friends for user 1.
Friend suggestion flow in Graph
In the above flow,
- First graph shows the current user i.e the Logged in User.
- Second graph show the Friends of the User with the connections.
- Final graph shows the suggested friends for the user based on the users friends of friends and also the mutual friends between them.
In the above program, all the users friends are represented as a Adjacency List. You can play around with the list and view the output for suggested friends and their mutual friends.
Now that we know the alogrithm for fetching the friends suggestions let’s implement this in the system that we have created already
PHP – Friend Suggestion Functions
From the above algorithm, we could implement the friends suggestion in the Relation
class that is already built.
Following are the additional methods added to that class to make this work,
getFriends(user_id)
– Will return the list of user’s friend id’s.getUser(user_id)
– Will return the user details of the given user id.getFriendSuggestions()
– This is the functions that actually fetches the friend suggestions for the currently logged in user.
.... .... ....
/**
* Get the friends id for the given user id.
*
* @param type $userId
* @return type
*/
public function getFriends($userId) {
$sql = 'SELECT * FROM `relationship` WHERE ' .
'(`user_one_id` = ' . $userId . ' OR `user_two_id` = '. $userId .') ' .
'AND `status` = 1';
$resultObj = $this->dbCon->query($sql);
$friends = array();
while($row = $resultObj->fetch_assoc()) {
if ($row['user_one_id'] !== $userId) {
$friends[] = $row['user_one_id'];
}
if ($row['user_two_id'] !== $userId) {
$friends[] = $row['user_two_id'];
}
}
return $friends;
}
/**
* Get the user details object for the given user id.
*
* @param $userId
* @return array|null
*/
public function getUser($userId) {
$user = null;
$sql = 'SELECT * FROM `users` WHERE `user_id` = ' . $userId;
$resultObj = $this->dbCon->query($sql);
if ($resultObj) {
$user = $resultObj->fetch_assoc();
}
return $user;
}
/**
* Get a list of suggested friends for the current user.
*/
public function getFriendSuggestions() {
$userId = $this->loggedInUser->getUserId();
$friends = $this->getFriends($userId);
$suggestedFriends = [];
foreach ($friends as $friendId) {
# Friends friends list.
$ff_list = $this->getFriends($friendId);
foreach ($ff_list as $ffriendId) {
# If the friendsFriend(ff) is not us, and not our friend, he can be suggested
if ($ffriendId != $userId && !in_array($ffriendId, $friends)) {
# The key is the suggested friend
$suggestedFriends[$ffriendId] = ['mutual_friends' => []];
$ff_friends = $this->getFriends($ffriendId);
foreach ($ff_friends as $ff_friendId) {
if (in_array($ff_friendId, $friends)) {
# If he is a friend of the current user, he is a mutual friend
$suggestedFriends[$ffriendId]['mutual_friends'][] = $ff_friendId;
}
}
}
}
}
# Convert the friend id's to user objects.
$suggestedFriendObjs = array();
if (!empty($suggestedFriends)) {
foreach ($suggestedFriends as $suggestedFriend => $mutualFriends) {
$suggestedFriendObj = new stdClass();
$suggestedFriendObj->suggestedUser = $this->getUser($suggestedFriend);
if (!empty($mutualFriends)) {
$mutualFriendObjs = [];
foreach ($mutualFriends['mutual_friends'] as $mutualFriend) {
$mutualFriendObjs[] = $this->getUser($mutualFriend);
}
}
$suggestedFriendObj->mutualFriends = $mutualFriendObjs;
$suggestedFriendObjs[] = $suggestedFriendObj;
}
}
return $suggestedFriendObjs;
}
... .. ...
Ajax Implementation
ajax/get_friend_suggestions.php
In the Ajax script, if the user exists it will fetch the suggested friends for the given user. This script uses the newly created methods in the Relation
to achieve this.
The response is returned as JSON so that it will easier for us to parse and display the result.
<?php
include_once('../app/config.php');
$response = [
'status' => false,
'data' => 'No friends list available for this user.'
];
if ($_SERVER['REQUEST_METHOD'] === 'GET' && isset($_GET['user_id'])) {
$user = new User();
$loggedInUser = $user->getUser($mysqli, (int) $_GET['user_id']);
# If the logged in user exists.
if ($loggedInUser) {
$relation = new Relation($mysqli, $loggedInUser);
$friendSuggestions = $relation->getFriendSuggestions();
$response['status'] = true;
$response['data'] = $friendSuggestions;
} else {
$response['data'] = 'User not available.';
}
}
header('Content-Type: application/json');
echo json_encode($response);
Javascript
js/app.js
Using jQuery ajax method, we call the get_friend_suggestions.php
script to fetch the list of friend suggestions and their mutual friends.
/**
* Helper method to get the cookie value for the given key.
*
* @param cname
*/
function getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for(var i=0; i<ca.length; i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1);
if (c.indexOf(name) == 0) return c.substring(name.length,c.length);
}
return "";
}
setTimeout(function() {
$.ajax({
method: 'GET',
url: 'ajax/get_friend_suggestions.php',
data: {
user_id: getCookie('uid')
},
success: function(data, status) {
var ul = $('#suggestedFriends');
// Remove the loading text
$('#sfLoading').remove();
if (status === 'success' && data.status) {
// Suggested friends string
var sfs = '';
data.data.forEach(function(item) {
// Suggested friend
var sf = '<li><a href="profile.php?uid=' + item.suggestedUser.user_id +
'">' + item.suggestedUser.username + '</a>';
// Check if there are any mutual friends.
if (item.mutualFriends.length > 0) {
// Mutual friend
var mfs = ' - <small>Mutual Friends ';
item.mutualFriends.forEach(function(mf) {
mfs += ' - <a href="profile.php?uid=' + mf.user_id + '">' + mf.username + '</a>';
});
mfs += '</small>';
}
sf += mfs + '</li>';
sfs += sf;
});
if (sfs == '') {
sfs = '<li>No suggested friends</li>';
}
ul.append(sfs);
} else {
ul.append('<li>No suggested friends</li>');
}
},
error: function(err) {
console.log(err);
}
});
}, 5000);
JSON Response
When we make the ajax request we get a json response as shown below.
The returned data
array contains the suggested friends objects. Each object contains the suggestedUser
and the mutual friends for that suggested user in the mutualFriends
field as an array.
{
"status":true,
"data":[
{
"suggestedUser":{
"user_id":"8",
"username":"Ezio Auditori"
},
"mutualFriends":[
{
"user_id":"2",
"username":"Johny Lare"
}
]
},
{
"suggestedUser":{
"user_id":"3",
"username":"Jennifer"
},
"mutualFriends":[
{
"user_id":"4",
"username":"Carl"
},
{
"user_id":"5",
"username":"Arya"
}
]
},
{
"suggestedUser":{
"user_id":"6",
"username":"Rick Mater"
},
"mutualFriends":[
{
"user_id":"5",
"username":"Arya"
}
]
}
]
}
Markup
The markup shows a simple Loading text for 5 seconds or so, then the app.js
script will make an ajax request and fetch the json data which will be used to populate the friends suggestion list.
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Suggested Friends</h3>
</div>
<div class="panel-body">
<ul id="suggestedFriends" style="list-style: none;">
<li id="sfLoading">Loading...</li>
</ul>
</div>
</div>
This system is not production ready, this is just to give you an idea of how to implement a system like this.
