Social Network Online Active Friends Feature Using PHP MySQL and jQuery

One of the important feature of Social Networks is to show which of your friends are online at the time so that you can connect with them. This is a amazing feature that increases the User Experience in the site. Here this system is implemented with a basic setup of a friends relationship and simple login system with a minimal functionality.


Database Design

Online Friends Database Schema

The database contains just 2 tables, users and friends this table contains the users relationships. The users table contains the fields,

  • is_loggedin - Whether the user is logged in.

  • last_active - When was the last time the user connected to the system.


SQL database shema code is provided in the download

OnlineFriends Class

This is the main class that performs most of the operations for the system. The system is implemented to get active users for 3 Minutes i.e 180 Seconds. If you need to extend the period of inactiveness you must change that in the getOnlineFriends method of the class.

<?php

class OnlineFriends {
  
  private $db;
  
  public function __construct() {
    $this->db = new mysqli('your-host', 'your-db-username', 'your-db-password',
        'online_friends');
  }
    
  /**
   * Update the last login in checkin time
   */
  public function updateLastCheckin($userId) {
    $query = 'UPDATE `users` SET `last_active` = NOW()' .
        'WHERE `id` = ' . (int)$userId;
    $result = $this->db->query($query);
  }
  
  /**
   * Check if the login is correct
   * @param String - User email id
   * @param String - Password for the user email
   */
  public function checkLogin($email, $password) {
    $cEmail = $this->db->real_escape_string($email);
    
    $query = "SELECT * FROM `users` 
          WHERE `users`.`email` = '{$cEmail}'";
    $result = $this->db->query($query);
    
    if ($result) {
      $row = $result->fetch_assoc();
      // Check if the credentials are correct.
      if (!empty($row) && $row['password'] === $password) {
        // Update the user login status
        $this->db->query('UPDATE `users` SET `is_loggedin` = 1 WHERE `id` = ' .
            $row['id']);
        return $row;
      }
    }
    
    return false;
  }
  
  /**
   * Get the list of user's friends
   * @param type $userId
   */
  public function getUserFriends($userId) {
    $query = 'SELECT * FROM `friends` WHERE (`user_one_id` = ' . (int)$userId
        . ' OR `user_two_id` = ' . (int)$userId . ') AND `status` = 1';
    $result = $this->db->query($query);
    
    $friends = array();
    if ($result) {
      while($row = $result->fetch_assoc()) {
        $friends[] = $row;
      }
      
      return $friends;
    }
  }
  
  /**
   * Get's list of online friends for the current user.
   * @param int $userId
   */
  public function getOnlineFriends($userId) {
    $friends = $this->getUserFriends($userId);
    
    $friend_ids = '(';
    
    if (!empty($friends)) {
      foreach ($friends as $friend) {
        if ($friend['user_one_id'] !== $userId) {
          $friend_ids .= $friend['user_one_id'] . ',';
        }
        if ($friend['user_two_id'] !== $userId) {
          $friend_ids .= $friend['user_two_id'] . ',';
        }
      }
    } else {
      return array();
    }
    
    // Gather the list of friends id's.
    $friend_ids = substr($friend_ids, 0, (strlen($friend_ids) - 1)) . ')';
    
    $query = 'SELECT * FROM `users` ' .
        'WHERE TIME_TO_SEC(TIMEDIFF(NOW(), `users`.`last_active`)) <= 180 ' .
        'AND `users`.`is_loggedin` = 1 ' .
        "AND `users`.`id` IN {$friend_ids}";
    $result = $this->db->query($query);
    
    if ($result) {
      $onlineFriends = array();
      while($row = $result->fetch_assoc()) {
        $onlineFriends[] = $row;
      }
      
      return $onlineFriends;
    }
    
    return array();
  }
  
  /**
   * Logout the user
   * @param type $userId
   */
  public function logout($userId) {
    $query = 'UPDATE `users` SET `users`.`is_loggedin` = 0 ' . 
              'WHERE `users`.`id` = ' . (int)$userId;
    $this->db->query($query);
  }

}

Ajax Components

update_stats.php

This returns the list of all the friends that are online for the currently logged in user using the methods in OnlineFriends

<?php
session_start();

$online_friends = array();

// Check if the user is logged in.
if (isset($_SESSION['user_id']) && isset($_COOKIE['user_id']) &&
    $_SESSION['user_id'] === $_COOKIE['user_id'] && 
    isset($_SESSION['user_id']) && $_SESSION['is_loggedin'] === true) {
  
  include_once __DIR__ . '/../includes/OnlineFriends.php';

  $app = new OnlineFriends();
  // Get the list of online friends for the current user.
  $online_friends = $app->getOnlineFriends($_SESSION['user_id']);
  $html = '';
  if (!empty($online_friends)) {
    foreach ($online_friends as $friend) {
      $html .= '<li class="list-group-item">' .
              '<span class="badge">•</span>' .
              $friend['name'] .
              '</li>';
    }
  }
  // Update the user's last active time.
  $app->updateLastCheckin($_SESSION['user_id']); 
}

// Send response to browser.
if ($html !== '') echo $html;

JavaScript

This code will send an ajax request to the update_stats.php every 5 second which could be changed in the setInterval method.

$(function() {
  
  function updateOnlineFriends() {
    $.ajax({
      url: 'ajax/update_stats.php',
      dataType: 'html',
      success: function(data, status) {
        if (data.length > 0) {
          $('#online-friends').html(data);
        } else {
          $('#online-friends').html('<li class="list-group-item">No friends online.</li>');
        }
      }
    });
  }
  
  // Update friends list every 3 seconds.
  setInterval(updateOnlineFriends, 5000);
  
});

HTML

index.php

<div class="container">
  <form class="form-signin" action="login.php" method="post">
    <h2 class="form-signin-heading">Please sign in</h2>
    <label for="inputEmail" class="sr-only">Email address</label>
    <input type="email" name="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
    <label for="inputPassword" class="sr-only">Password</label>
    <input type="password" id="inputPassword" class="form-control" placeholder="Password" name="password" required>
    <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
  </form>
  <div class="row">
    <div class="col-row3"></div>
    <div class="col-row6">
      <?php
      if (isset($_GET['error']) && $_GET['error'] === true) {
        echo '<p class="alert alert-error">Invalid email or password..!</p>';
      }
      ?>
    </div>
    <div class="col-row3"></div>
  </div>
</div>

home.php

<?php
session_start();

if (isset($_SESSION['user_id']) && isset($_COOKIE['user_id']) &&
    $_SESSION['user_id'] === $_COOKIE['user_id'] && 
    isset($_SESSION['is_loggedin']) && $_SESSION['is_loggedin'] === true) {

  include_once __DIR__ . '/includes/OnlineFriends.php';

  $app = new OnlineFriends();
  $online_friends = $app->getOnlineFriends($_SESSION['user_id']);
} else {
  header('Location: index.php');
}
?>
......
......
<div class="inner cover">
  <h1 class="cover-heading">Online Friends</h1>
  <div class="row">
    <div class="col-md-3"></div>
    <div class="col-md-6">
      <ul class="list-group" id="online-friends">
        <?php
        if (!empty($online_friends)) {
          foreach ($online_friends as $friend) {
            echo '<li class="list-group-item">' .
                 '<span class="badge">•</span>' .
                 $friend['name'] .
                 '</li>';
          }
        } else {
          echo '<li class="list-group-item">No friends online.</li>';
        }
        ?>
      </ul>
    </div>
    <div class="col-md-3"></div>
  </div>
</div>

Other Components

login.php

<?php
session_start();

include_once __DIR__ . '/includes/OnlineFriends.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST' && !empty($_POST)) {
  $email = $_POST['email'];
  $password = $_POST['password'];
  
  $app = new OnlineFriends();
  $loginUser = $app->checkLogin($email, $password);
  
  if ($loginUser !== false && !empty($loginUser)) {
    // Update the last checkin stats.
    $app->updateLastCheckin($loginUser['id']);
    $_SESSION['user_id'] = $loginUser['id'];
    $_SESSION['is_loggedin'] = true;
    setcookie('user_id', $loginUser['id'], 0, '/', null, false, true);
    setcookie('is_loggedin', true, 0, '/', null, false, true);
    header('Location: home.php');
  } else {
    header('Location: index.php?error=true');
  }
} else {
  header('Location: index.php?error=true');
}

logout.php

<?php
session_start();

include_once __DIR__ . '/includes/OnlineFriends.php';

$app = new OnlineFriends();
$app->logout($_SESSION['user_id']);

// Expire session
session_destroy();
$_COOKIE = array();
$_SESSION = array();

header('Location: index.php');

This is just one way to implement this system. There are lot's of other better ways this could be implemented. Leave your thoughts in comments.