Simple Like Dislike System In PHP MySQL (Free Download)

Welcome to a tutorial on how to build a simple like and dislike rating system with PHP and MySQL. Social media is big these days, and I am sure you have already seen “upvote/downvote” or “like/dislike” features everywhere. If you want to create your own independent like/dislike system, let us walk through an example – Read on!

 

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Here is the download link to the example code, so you don’t have to copy-paste everything.

 

EXAMPLE CODE DOWNLOAD

Source code on GitHub Gist

Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

SORRY FOR THE ADS...

But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.

Buy Me A Coffee Code Boxx eBooks

 

 

PHP MYSQL LIKE DISLIKE SYSTEM

All right, let us now get into more details of building a like-dislike system with PHP and MYSQL.

 

TUTORIAL VIDEO

 

PART 1) REACTIONS DATABASE TABLE

1-database.sql
CREATE TABLE `reactions` (
  `id` bigint(20) NOT NULL,
  `user_id` bigint(20) NOT NULL,
  `reaction` tinyint(1) NOT NULL DEFAULT '1'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `reactions`
  ADD PRIMARY KEY (`id`,`user_id`),
  ADD KEY `reaction` (`reaction`);
Field Description
id Primary and foreign key. The post ID, product ID, video ID – Whatever you want to add a like/dislike button to.
user_id Primary and foreign key. The user who liked/disliked.
reaction For this example, we will use -1 for dislike and 1 for like.

 

PART 2) PHP LIKES/DISLIKES LIBRARY

2-lidi-lib.php
<?php
class Reactions {
  // (A) CONSTRUCTOR - CONNECT TO DATABASE
  private $pdo;
  private $stmt;
  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_NAMED
    ]);
  }

  // (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
  function __destruct () {
    $this->pdo = null;
    $this->stmt = null;
  }

  // (C) HELPER FUNCTION - RUN SQL
  function query ($sql, $data=null) : void {
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($data);
  }
 
  // (D) GET REACTIONS FOR ID
  function get ($id, $uid=null) {
    // (D1) GET TOTAL REACTIONS
    $reacts = ["react" => [0, 0]]; // [likes, dislikes]
    $this->query(
      "SELECT `reaction`, COUNT(`reaction`) `total`
      FROM `reactions` WHERE `id`=?
      GROUP BY `reaction`", [$id]
    );
    while ($r = $this->stmt->fetch()) {
      if ($r["reaction"]==1) { $reacts["react"][0] = $r["total"]; }
      else { $reacts["react"][1] = $r["total"]; }
    }

    // (D2) GET REACTION BY USER (IF SPECIFIED)
    if ($uid != null) {
      $this->query(
        "SELECT `reaction` FROM `reactions` WHERE `id`=? AND `user_id`=?",
        [$id, $uid]
      );
      $reacts["user"] = $this->stmt->fetchColumn();
    }
 
    // (D3) DONE - RETURN RESULTS
    return $reacts;
  }
 
  // (E) SAVE REACTION
  function save ($id, $uid, $react) {
    // (E1) FORMULATE SQL
    if ($react == 0) {
      $sql = "DELETE FROM `reactions` WHERE `id`=? AND `user_id`=?";
      $data = [$id, $uid];
    } else {
      $sql = "REPLACE INTO `reactions` (`id`, `user_id`, `reaction`) VALUES (?,?,?)";
      $data = [$id, $uid, $react];
    }
 
    // (E2) EXECUTE SQL
    $this->query($sql, $data);
    return true;
  }
}
 
// (F) 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", "");
 
// (G) NEW REACTIONS OBJECT
$_REACT = new Reactions();

The library looks massive at first, but keep calm and look carefully.

  • (A, B, G) On creating $_REACT = new Reactions(), the constructor will automatically connect to the database. The destructor closes the connection.
  • (C) query() is a helper function to run an SQL query.
  • (D & E) There are only 2 “actual functions”.
    • get() Gets the total number of reactions (likes and dislikes) for the given post/video/product ID. If the user ID is provided, it will also get that user’s reaction.
    • save() Self-explanatory, save a user’s reaction. Set $react = 0 to remove the user’s reaction.
  • (F) Remember to change the database settings to your own.

 

 

PART 3) AJAX HANDLER

3-lidi-ajax.php
if (isset($_POST["req"])) {
  // (A) LOAD LIKE DISLIKE LIBRARY
  require "2-lidi-lib.php";
 
  // (B) FIXED DUMMY USER ID
  $uid = 1;
 
  // (C) HANDLE REQUESTS
  switch ($_POST["req"]) {
    // (C1) GET REACTIONS
    case "get":
      echo json_encode($_REACT->get($_POST["pid"], $uid));
      break;
 
    // (C2) SAVE REACTION
    case "save":
      if ($_REACT->save($_POST["pid"], $uid, $_POST["react"])) {
        echo json_encode($_REACT->get($_POST["pid"], $uid));
      } else { echo $_REACT->error ; }
      break;
}}

Next, we have an AJAX handler to “map” user requests to the library functions.

  • That is, send a request $_POST["req"] and the required parameters over, and this will process it accordingly.
  • A quick example of those who are lost – We send $_POST["req"]="save", $_POST["pid"]=900, and $_POST["react"]=1 to this script when the user likes the product.

P.S. Take note that the user ID is fixed in this example. You should be tying this into your own system, and opening the like/dislike feature to registered users only.

 

 

STEP 4) DUMMY PRODUCT PAGE

4A) THE HTML

4-demo.php
<!-- (A) LOAD LIKE/DISLIKE "WIDGET" -->
<link rel="stylesheet" href="lidi.css">
<script src="lidi.js"></script>
<script src="4-demo.js"></script>

<!-- (B) DUMMY PRODUCT -->
<div id="demowrap">
  <h1>PRODUCT</h1>
  <img src="box.png">
  <div id="demo"></div>
</div>

The only important part here is using <div id="demo"> to generate the like/dislike button. Also, lidi.js and lidi.css is a like/dislike widget that I created. Check the links below if you are interested to learn more.

 

 

4B) THE JAVASCRIPT

4-demo.js
var demo = {
  // (A) SUPPORT FUNCTION - AJAX FETCH
  fetch : (data, load) => {
    // (A1) FORM DATA
    var form = new FormData();
    for (let [k,v] of Object.entries(data)) { form.append(k, v); }

    // (A2) AJAX FETCH
    fetch("2-lidi-lib.php", { method: "post", body : form })
    .then(res => res.text())
    .then(res => load(res))
    .catch(err => console.error(err));
  },

  // (B) INIT
  lidi : null, // like dislike instance
  init : () => demo.fetch(
    // (B1) GET LIKES COUNT FROM SERVER
    {
      req : "get",
      pid : 900 // fixed dummy product id
    },
 
    // (B2) DRAW HTML LIKE/DISLIKE BUTTON
    data => {
      demo.lidi = lidi({
        hWrap : document.getElementById("demo"),
        status : data.user ? data.user : 0,
        count : data.react,
        change : demo.save
      });
    }
  ),
 
  // (C) SAVE LIKE/DISLIKE REACTION
  save : status => demo.fetch(
    // (C1) SEND NEW STATUS TO SERVER
    {
      req : "save",
      react : status,
      pid : 900 // fixed dummy product id
    },
 
    // (C2) UPDATE HTML COUNT
    res => demo.lidi.recount(res.react)
  )
};
window.onload = demo.init;

Not going to explain this line-by-line, so a quick walkthrough:

  1. demo.fetch() A helper function to do an AJAX call to 2-lidi-lib.php.
  2. demo.init() Runs on page load. It gets the like/dislike count from the server and sets up the HTML widget.
  3. demo.save() This is fired when the user changes the like/dislike reaction. Once again, we will send the new reaction to update the server.

 

 

EXTRAS

That’s all for the code, and here are a few small extras that may be useful to you.

 

A SMALL NOTE REGARDING THE COUNT

  • As you can see, we are doing a COUNT(*) to get the total number of likes and dislikes.
  • We get the likes/dislikes count when the user first loads the page.
  • Also when the user changes the like/dislike status.

This works for small projects. But there’s a good reason why most social media remove exact and accurate reaction counts – Counting millions of likes is very heavy on system resources. So if you ever get to that stage, do consider other ways to deal with the likes/dislikes count. Maybe cache the SQL, or create a “total count table”.

 

MUST THERE BE A USER SYSTEM?

Technically, we can generate a random ID and set it in a cookie – Use this as a “temporary unique user ID”. But the security concern remains, anyone who knows well enough can create a simple bot to repeat “like and delete the cookie”.

There will be a lot of spam if you open the like/dislike system to the public. So yes, I will recommend keeping like/dislike as a “users-only feature”.

 

HOW ABOUT MULTIPLE PRODUCTS?

<!-- (A) HTML PRODUCTS -->
<div class="pdt" data-pid="1"></div>
<div class="pdt" data-pid="2"></div>
 
<script>
// (B) ATTACH LIKE/DISLIKE BUTTONS
for (let p of document.querySelectorAll(".pdt")) {
  lidi({
    hWrap : p,
    change : stat => demo.fetch({
      req : "save",
      react : stat,
      pid : p.dataset.pid
    })
  });
}
</script>

But of course, this is only a simple example. There are endless ways to do it – Just make sure that the proper product ID is associated with the like/dislike button and passed to the server “on reaction change”.

 

LINKS & REFERENCES

 

THE END

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you to improve your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

21 thoughts on “Simple Like Dislike System In PHP MySQL (Free Download)”

  1. hello what if the i want to use the system in a loop
    like in my post page i am using a while loop to display all publish post

  2. “If you do not want to build a user system, you really should be looking at the Facebook Like Button for web pages.” what’s special with facebook? how to control spam? thank you.

    1. 1) You don’t need to create your own user and likes system, but users need to have an FB account.
      2) All data is held on FB.
      3) FB will deal with the spam and updates, you just implement the API and relax.

  3. Thanks for the great code. In my scenario I will add to different products. But likes and dislikes are the same everywhere. How to set it for each product?

  4. Many thanks! This is easy to implement just what I was looking for. Because of a brain damage it is not possible for me anymore to understand all the code but this is very clear and easy to implement. I am wondering if it is possible to use the <alt tag for the images so people who have non or no good visibility also know what buttons there are when their device speaks out the text. And if possible how do I implement this?

Leave a Comment

Your email address will not be published. Required fields are marked *