Skip to content

Instantly share code, notes, and snippets.

@code-boxx
Last active May 26, 2023 05:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save code-boxx/cf0d3da12359853cf03c8d442a3da4e8 to your computer and use it in GitHub Desktop.
Save code-boxx/cf0d3da12359853cf03c8d442a3da4e8 to your computer and use it in GitHub Desktop.
PHP MYSQL Friend Relationship System

PHP MYSQL FRIEND RELATIONSHIP SYSTEM

https://code-boxx.com/friend-system-php-mysql/

IMAGES

funny

NOTES

  1. Create a database and import 1-database.sql.
  2. Change the database settings in 2-lib-relation.php to your own.
  3. Launch 3a-friends.php in the browser.

LICENSE

Copyright by Code Boxx

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

-- (A) USERS
CREATE TABLE `users` (
`user_id` bigint(20) NOT NULL,
`user_name` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `users`
ADD PRIMARY KEY (`user_id`),
ADD KEY `user_name` (`user_name`);
ALTER TABLE `users` CHANGE `user_id` `user_id` BIGINT(20) NOT NULL AUTO_INCREMENT;
-- (B) RELATIONS
CREATE TABLE `relation` (
`from` bigint(20) NOT NULL,
`to` bigint(20) NOT NULL,
`status` varchar(1) NOT NULL,
`since` datetime NOT NULL DEFAULT current_timestamp()
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
ALTER TABLE `relation`
ADD PRIMARY KEY (`from`,`to`,`status`),
ADD KEY `since` (`since`);
-- (C) DUMMY EXAMPLES
-- (C1) DUMMY USERS
INSERT INTO `users` (`user_id`, `user_name`) VALUES
(1, 'Job Doe'),
(2, 'Joe Doe'),
(3, 'Jon Doe'),
(4, 'Joy Doe');
-- (C2) DUMMY RELATIONS
INSERT INTO `relation` (`from`, `to`, `status`) VALUES
-- "P"ENDING FRIEND REQUEST
-- JOB SENDS A FRIEND REQUEST TO JOE
(1, 2, 'P'),
-- JOY SENDS A FRIEND REQUEST TO JOB
(4, 1, 'P'),
-- "F"RIENDS
-- JOE & JON ARE FRIENDS
(2, 3, 'F'),
(3, 2, 'F'),
-- JOE & JOY ARE FRIENDS
(2, 4, 'F'),
(4, 2, 'F'),
-- "B"LOCKED
-- JOE FINDS JON ANNOYING & BLOCKS HIM
(2, 3, 'B');
<?php
class Relation {
// (A) CONSTRUCTOR - CONNECT TO DATABASE
private $pdo = null;
private $stmt = null;
public $error = "";
function __construct () {
$this->pdo = new PDO(
"mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
DB_USER, DB_PASSWORD, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
}
// (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
function __destruct () {
if ($this->stmt!==null) { $this->stmt = null; }
if ($this->pdo!==null) { $this->pdo = null; }
}
// (C) HELPER FUNCTION - EXECUTE SQL QUERY
function query ($sql, $data=null) : void {
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute($data);
}
// (D) SEND FRIEND REQUEST
function request ($from, $to) {
// (D1) CHECK IF ALREADY FRIENDS
$this->query(
"SELECT * FROM `relation` WHERE `from`=? AND `to`=? AND `status`='F'",
[$from, $to]
);
if (is_array($this->stmt->fetch())) {
$this->error = "Already added as friends";
return false;
}
// (D2) CHECK FOR PENDING REQUESTS
$this->query(
"SELECT * FROM `relation` WHERE
(`status`='P' AND `from`=? AND `to`=?) OR
(`status`='P' AND `from`=? AND `to`=?)",
[$from, $to, $to, $from]
);
if (is_array($this->stmt->fetch())) {
$this->error = "Already has a pending friend request";
return false;
}
// (D3) ADD FRIEND REQUEST
$this->query(
"INSERT INTO `relation` (`from`, `to`, `status`) VALUES (?, ?, 'P')",
[$from, $to]
);
return true;
}
// (E) ACCEPT FRIEND REQUEST
function acceptReq ($from, $to) {
// (E1) UPGRADE STATUS TO "F"RIENDS
$this->query(
"UPDATE `relation` SET `status`='F' WHERE `status`='P' AND `from`=? AND `to`=?",
[$from, $to]
);
if ($this->stmt->rowCount()==0) {
$this->error = "Invalid friend request";
return false;
}
// (E2) ADD RECIPOCAL RELATIONSHIP
$this->query(
"INSERT INTO `relation` (`from`, `to`, `status`) VALUES (?, ?, 'F')",
[$to, $from]
);
return true;
}
// (F) CANCEL FRIEND REQUEST
function cancelReq ($from, $to) {
$this->query(
"DELETE FROM `relation` WHERE `status`='P' AND `from`=? AND `to`=?",
[$from, $to]
);
return true;
}
// (G) UNFRIEND
function unfriend ($from, $to) {
$this->query(
"DELETE FROM `relation` WHERE
(`status`='F' AND `from`=? AND `to`=?) OR
(`status`='F' AND `from`=? AND `to`=?)",
[$from, $to, $to, $from]
);
return true;
}
// (H) BLOCK & UNBLOCK
function block ($from, $to, $blocked=true) {
// (H1) BLOCK
if ($blocked) { $this->query(
"INSERT INTO `relation` (`from`, `to`, `status`) VALUES (?, ?, 'B')",
[$from, $to]
); }
// (H2) UNBLOCK
else { $this->query(
"DELETE FROM `relation` WHERE `from`=? AND `to`=? AND `status`='B'",
[$from, $to]
);}
// (H3) DONE
return true;
}
// (I) GET FRIEND REQUESTS
function getReq ($uid) {
// (I1) GET OUTGOING FRIEND REQUESTS (FROM USER TO OTHER PEOPLE)
$req = ["in"=>[], "out"=>[]];
$this->query(
"SELECT * FROM `relation` WHERE `status`='P' AND `from`=?",
[$uid]
);
while ($row = $this->stmt->fetch()) { $req["out"][$row["to"]] = $row["since"]; }
// (I2) GET INCOMING FRIEND REQUESTS (FROM OTHER PEOPLE TO USER)
$this->query(
"SELECT * FROM `relation` WHERE `status`='P' AND `to`=?",
[$uid]
);
while ($row = $this->stmt->fetch()) { $req["in"][$row["from"]] = $row["since"]; }
return $req;
}
// (J) GET FRIENDS & FOES (BLOCKED)
function getFriends ($uid) {
// (J1) GET FRIENDS
$friends = ["f"=>[], "b"=>[]];
$this->query(
"SELECT * FROM `relation` WHERE `status`='F' AND `from`=?", [$uid]
);
while ($row = $this->stmt->fetch()) { $friends["f"][$row["to"]] = $row["since"]; }
// (J2) GET FOES
$this->query(
"SELECT * FROM `relation` WHERE `status`='B' AND `from`=?", [$uid]
);
while ($row = $this->stmt->fetch()) { $friends["b"][$row["to"]] = $row["since"]; }
return $friends;
}
// (K) GET ALL USERS
function getUsers () {
$this->query("SELECT * FROM `users`");
$users = [];
while ($row = $this->stmt->fetch()) { $users[$row["user_id"]] = $row["user_name"]; }
return $users;
}
}
// (L) DATABASE SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8mb4");
define("DB_USER", "root");
define("DB_PASSWORD", "");
// (M) NEW RELATION OBJECT
$REL = new Relation();
<!DOCTYPE html>
<html>
<head>
<title>DUMMY MANAGE FRIENDS PAGE</title>
<meta charset="utf-8">
<script src="3b-friends.js"></script>
<link rel="stylesheet" href="3c-friends.css">
</head>
<body>
<?php
// (A) LOAD RELATIONSHIP LIBRARY + SET CURRENT USER
// @TODO - TIE INTO YOUR OWN USER SYSTEM & SESSION
require "2-lib-relation.php";
$uid = 1;
// (B) PROCESS RELATIONSHIP REQUEST
if (isset($_POST["req"])) {
$pass = true;
switch ($_POST["req"]) {
// (B0) INVALID
default: $pass = false; $REL->error = "Invalid request"; break;
// (B1) ADD FRIEND
case "add": $pass = $REL->request($uid, $_POST["id"]); break;
// (B2) ACCEPT FRIEND
case "accept": $pass = $REL->acceptReq($_POST["id"], $uid); break;
// (B3) CANCEL ADD
case "cancel": $pass = $REL->cancelReq($uid, $_POST["id"]); break;
// (B4) UNFRIEND
case "unfriend": $pass = $REL->unfriend($uid, $_POST["id"], false); break;
// (B5) BLOCK
case "block": $pass = $REL->block($uid, $_POST["id"]); break;
// (B6) UNBLOCK
case "unblock": $pass = $REL->block($uid, $_POST["id"], false); break;
}
echo $pass ? "<div class='ok'>OK</div>" : "<div class='nok'>{$REL->error}</div>";
}
// (C) GET + SHOW ALL USERS
$users = $REL->getUsers(); ?>
<!-- (C1) CURRENT USER -->
<div id="userNow" class="flex">
<div><img src="funny.png"></div>
<div>
<small>you are:</small><br>
<strong><?=$users[$uid]?></strong>
</div>
</div>
<!-- (C2) USER LIST -->
<div id="userList"><?php
$requests = $REL->getReq($uid);
$friends = $REL->getFriends($uid);
foreach ($users as $id=>$name) { if ($id != $uid) {
echo "<div class='uRow flex'>";
// (C2-1) USER ID & NAME
echo "<div class='uName'>$id) $name</div>";
// (C2-2) BLOCK/UNBLOCK
if (isset($friends["b"][$id])) {
echo "<button onclick=\"relate('unblock', $id)\">Unblock</button>";
} else {
echo "<button onclick=\"relate('block', $id)\">Block</button>";
}
// (C2-3) FRIEND STATUS
// FRIENDS
if (isset($friends["f"][$id])) {
echo "<button onclick=\"relate('unfriend', $id)\">Unfriend</button>";
}
// INCOMING FRIEND REQUEST
else if (isset($requests["in"][$id])) {
echo "<button onclick=\"relate('accept', $id)\">Accept Friend</button>";
}
// OUTGOING FRIEND REQUEST
else if (isset($requests["out"][$id])) {
echo "<button onclick=\"relate('cancel', $id)\">Cancel Add</button>";
}
// STRANGERS
else {
echo "<button onclick=\"relate('add', $id)\">Add Friend</button>";
}
echo "</div>";
}}
?></div>
<!-- (D) NINJA RELATIONSHIP FORM -->
<form id="ninform" method="post" target="_self">
<input type="hidden" name="req" id="ninreq">
<input type="hidden" name="id" id="ninid">
</form>
</body>
</html>
function relate (req, uid) {
document.getElementById("ninreq").value = req;
document.getElementById("ninid").value = uid;
document.getElementById("ninform").submit();
}
/* (A) WHOLE PAGE */
* {
font-family: Arial, Helvetica, sans-serif;
box-sizing: border-box;
}
body { padding: 10px; }
.flex {
display: flex;
align-items: center;
}
/* (B) NOTIFICATION BAR */
.ok, .nok {
padding: 10px;
margin-bottom: 10px;
}
.ok {
border: 1px solid #7ae9a7;
background: #d7ffe7;
}
.nok {
border: 1px solid #e97a7a;
background: #ffd7d7;
}
/* (C) USERS */
/* (C1) CURRENT USER */
#userList, #userNow { max-width: 400px; }
#userNow {
padding: 10px;
color: #fff;
background: #2e2e2e;
}
#userNow img {
width: 48px;
border-radius: 50%;
margin-right: 10px;
}
#userNow strong {
text-transform: uppercase;
font-size: 24px;
}
/* (C2) USERS LIST */
.uRow {
padding: 10px;
background: #f5f5f5;
border: 1px solid #dbdbdb;
margin-top: 15px;
}
.uName { flex-grow: 1; }
.uRow button {
width: 80px;
height: 50px;
margin-left: 5px;
border: 0;
color: #fff;
background: #3069d2;
cursor: pointer;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment