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. Dear Mr. Toh, thank you so much for your tutorials and all the stuff. And also for your wise words in the help and FAQs. Could you (or someone else) please recommend me a site to learn about the $_SESSION for using it in my own project (I’m an absolute beginner but could understand all this up to here)? Is there a way to donate? Many thanks and best wishes from Germany! Andreas

    1. The long learning process for absolute beginners to fully understand:

      1) Start with cookies – https://code-boxx.com/cookies-in-php/
      2) Then, PHP sessions (sessions are built on top of cookies) – https://code-boxx.com/sessions-in-php/
      3) If you are not familiar with OOP, just about PHP OOP tutorial on the Internet – https://www.google.com/search?q=php+oop+tutorial
      4) Working with MySQL database in PHP – https://www.google.com/search?q=php+mysql+pdo+tutorial
      5) Javascript AJAX FETCH – https://www.digitalocean.com/community/tutorials/how-to-use-the-javascript-fetch-api-to-get-data
      6) A simple user login system (the part on user ID, to track user likes and dislikes) – https://code-boxx.com/simple-login-php-mysql/
      7) Or another way to track users without session using JSON Web Tokens (JWT) – https://code-boxx.com/jwt-login-authentication-php-mysql/

    2. Many thanks! I already started to learn php from the beginning. It is quiet interesting and I will see whether an old man can learn it any more 😆. I already have installed the great script SITELOCK for users login (like on my page). Maybe I can use this in combination with your code. Ich will try to find out. All the best for you, Mr. Toh, and again: Is there a way to donate (for you)?!

      Best wishes for 2022 from Germany! Andreas

    1. Yes we can, i have modified the code and it work perfectly, but if u use firefox the code didnt work

    2. No problems here with Chrome, Opera, Edge, and Firefox – Not sure which version of Firefox you are using… Your customizations probably broke it.

Leave a Comment

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