4 Steps Simple Comment System In PHP MYSQL (Free Download)

Welcome to a tutorial on how to create a simple comment system using PHP, MYSQL, and AJAX. Want to upgrade your website or eCommerce store? Allow users to enter comments on a post or product? Well, it really isn’t too difficult to create a comment (or feedback) system from scratch – Let us walk through a simple one in this guide, read on!

 

 

TABLE OF CONTENTS

 

PHP MYSQL COMMENTS SYSTEM

All right, let us now get into the steps of creating an easy AJAX-driven comments system with PHP and MYSQL.

 

 

STEP 1) COMMENTS DATABASE TABLE

1-comments.sql
CREATE TABLE `comments` (
  `comment_id` bigint(20) NOT NULL,
  `post_id` bigint(20) NOT NULL,
  `timestamp` datetime NOT NULL DEFAULT current_timestamp(),
  `name` varchar(255) NOT NULL,
  `message` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

ALTER TABLE `comments`
  ADD PRIMARY KEY (`comment_id`);

ALTER TABLE `comments`
  MODIFY `comment_id` bigint(20) NOT NULL AUTO_INCREMENT;
Field Description
comment_id Primary key and auto-increment.
post_id Foreign key. Use this to identify which comments belong to which post (or product).
timestamp Date and time when the comment is posted.
name Name of the user.
message Comment message.

This should be easy enough, but you might want to change the structure a little to fit your own project. For example, it may make sense to add an email field, or use URL slugs instead of ID (change post_id to post_slug, use VARCHAR instead of BIGINT).

 

 

STEP 2) PHP COMMENT LIBRARY

2-lib.php
<?php
class Comments {
  // (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 () {
    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }
 
  // (C) HELPER - RUN SQL QUERY
  function query ($sql, $data=null) : void {
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($data);
  }
 
  // (D) GET COMMENTS
  function get ($id) {
    $this->query(
      "SELECT `name`, `timestamp`, `message`
       FROM `comments` WHERE `post_id`=?
       ORDER BY `timestamp` ASC", [$id]);
    return $this->stmt->fetchAll();
  }
 
  // (E) SAVE COMMENT
  function save ($id, $name, $msg) {
    $this->query(
      "INSERT INTO `comments` (`post_id`, `name`, `message`) VALUES (?,?,?)",
      [$id, $name, strip_tags($msg)]
    );
    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) COMMMENTS OBJECT
$_COM = new Comments();

This may look complicated at first, but keep calm and look carefully.

  • (A, B, G) When $_COM = new Comments() is created, the constructor connects to the database. The destructor closes the connection.
  • (C) query() A helper function to run an SQL query.
  • (D & E) There are only 2 “actual comment functions” here.
    • get() Get all comments for the given post/product/content ID.
    • save() Save a comment.
  • (F) Remember to change the settings to your own.

 

 

STEP 3) COMMENT AJAX HANDLER

3-ajax.php
<?php
if (isset($_POST["req"])) {
  require "2-lib.php";
  switch ($_POST["req"]) {
    // (A) GET COMMENTS
    case "get";
      echo json_encode($_COM->get($_POST["id"]));
      break;
 
    // (B) ADD COMMENT
    case "add":
      $_COM->save($_POST["id"], $_POST["name"], $_POST["msg"]);
      echo "OK";
      break;
}}

How this AJAX handler works is very simple, just post the request followed by the required parameters. For example:

  • To get the comments – $_POST = ["req"=>"get", "pid"=>888]
  • To add a comment – $_POST = ["req"=>"add", "pid"=>888, "name"=>"John", "msg"=>"Hello"]

 

 

STEP 4) DUMMY COMMENTS PAGE

4A) THE HTML

4a-comments.html
<!-- (A) GIVE THIS PAGE A HIDDEN POST ID -->
<input type="hidden" id="pid" value="999">
 
<!-- (B) COMMENTS WILL LOAD HERE -->
<div id="cWrap"></div>
 
<!-- (C) ADD NEW COMMENT -->
<form id="cAdd" onsubmit="return comments.add(this)">
  <input type="text" id="cName" placeholder="Name" required>
  <textarea id="cNsg" placeholder="Message" required></textarea>
  <input type="submit" value="Post Comment">
</form>
  1. Tag the page with a unique ID, place it into a hidden field – <input type="hidden" id="pid" value="N">.
  2. Create a container to load all the comments – <div id="cWrap">.
  3. Finally, the “Add A Comment” form itself – <form id="cAdd">.

 

 

4B) COMMENTS JAVASCRIPT

4b-comments.js
var comments = {
  // (A) HELPER - AJAX CALL
  ajax : (data, after) => {
    // (A1) DATA TO SEND
    var form = new FormData();
    form.append("id", document.getElementById("pid").value);
    for (let [k,v] of Object.entries(data)) { form.append(k,v); }
 
    // (A2) AJAX FETCH
    fetch("3-ajax.php", { method: "POST", body: form })
    .then(res => res.text()).then(after)
    .catch(err => console.error(err));
  },
 
  // (B) LOAD COMMENTS INTO <DIV ID="CWRAP">
  load : () => comments.ajax({ req : "get" }, data => {
    let wrap = document.getElementById("cWrap"), row;
    data = JSON.parse(data);
    wrap.innerHTML = "";
    if (data.length > 0) { for (let c of data) {
      row = document.createElement("div");
      row.className = "row";
      row.innerHTML = `<img class="cImg" src="talk.png">
      <div class="cTxt">
        <div class="cMsg">${c.message}</div>
        <div>
          <span class="cName">${c.name}</span>
          <span class="cTime">| ${c.timestamp}</span>
        </div>
      </div>`;
      wrap.appendChild(row);
    }}
  }),
 
  // (C) ADD COMMENT
  add: () => {
    comments.ajax({
      req : "add",
      name : document.getElementById("cName").value,
      msg : document.getElementById("cMsg").value
    }, res => {
      if (res == "OK") {
        document.getElementById("cAdd").reset();
        comments.load();
      } else { alert(res); }
    });
    return false;
  }
};
window.onload = comments.load;

Some beginners may panic at the sight of Javascript, but it is actually simple and straightforward again.

  1. We are using AJAX to drive the comments, and thus the need for this comments.ajax() helper function. Yep, all it does is nicely pack the data and send an AJAX request to 2-ajax.php.
  2. On window load, comments.load() will fire up. As you can guess, this does an AJAX call to the server to load the comments for the current page.
  3. The comments.add() function is fired when the user adds a new comment. Again, this is AJAX-driven.

 

 

DOWNLOAD & NOTES

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

 

SUPPORT

600+ free tutorials & projects on Code Boxx and still growing. I insist on not turning Code Boxx into a "paid scripts and courses" business, so every little bit of support helps.

Buy Me A Meal Code Boxx eBooks

 

EXAMPLE CODE DOWNLOAD

Click here for the 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.

 

EXTRA BITS & LINKS

We are finally done with the entire system, and here are some extras that may be useful to you.

 

IMPROVEMENT IDEAS & FAQ

Yes, this simple example works, but it is far from a “professional system”. There is plenty you can do to make it better:

  • Need some spam protection? Check out Google reCAPTCHA.
  • Sort comments – Change ORDER BY `timestamp` ASC/DESC in function get() 2-lib.php.
  • Only registered users can post.
    • In 4a-comments.php, show the “add comments” form to registered users only. E.G. if (isset($_SESSION["user"])) { DRAW <FORM ID="CADD"> }.
    • In 2-ajax.php, process “save comment” only if the user is signed in. E.G. if (isset($_SESSION["user"])) { $_COMMENT->save(...); }
  • Manual approval required.
    • Add an approved flag to the comments table, set the default value to 0.
    • In 2-lib.php, change get() to only fetch approved comments – WHERE `post_id=? AND `approved`=1.
    • Add a new library function to approve/delete comments.
  • Add more of your own features and functions –
    • Moderation (auto censor bad words)
    • Allow users to edit their own comment?
    • Allow users to delete their own comment?
    • Introduce admin or moderators?

Yep, the possibilities are endless. It’s up to you to complete your own system now.

 

REPLY TO COMMENTS

Here is a possibility:

  • Add parent_id to the comments table – If it is a “base comment”, parent_id will be 0. If it is a reply, parent_id will hold the comment_id that it replies to.
  • Change function get(), find some way to properly arrange the data. E.G. $comments = [COMMENT ID => COMMENT]; $replies = [PARENT ID => [ARRAY OF COMMENT ID]].
  • Redo the entire comments page. Draw nested comments with replies, and add a reply button to the comments.
  • Find some way to control the number of levels – So you don’t get “a reply to a reply that replies to yet another reply”.

Good luck, and feel free to challenge yourself. There’s a good reason why “reply” is left out to keep this tutorial simple. 😆

P.S. This will probably require recursion to work properly. See my other tutorial on category/subcategory if you are interested to learn more.

 

LINKS & REFERENCES

 

THE END

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

80 thoughts on “4 Steps Simple Comment System In PHP MYSQL (Free Download)”

  1. Are you a Business Man, woman, politician, pastor, musician artist, student and you want to be Successfully Rich, famous and powerful in life?
    You can achieve your dreams by being a member of the great brotherhood of the Illuminati.🔺🔯🔺.

    1. No, I am only a poor idiot. Can I still join though? Like… Get a lot of free money and some of those “powerful stuff”?

      Nah. On second thoughts, you guys are just sexist with all that “great brotherhood” stuff. Also, I am already living my dream as an Internet Junkie. 😆

  2. EDIT : MULTIPLE COMMENTS COMBINED

    (A) In sql file: int() should have no arguments – int. utf8 should be utf8mb4.
    Add path to folder if different when adding to :
    Changed to for Comments title. CSS: h4 { font-size: 16px; margin: 0 0 1px 0; text-transform: uppercase; color: #4660E2 }
    Changed to for Reply title. CSS: h5 { font-size: 16px; margin: 0 0 1px 0; text-transform: uppercase; color: #000000 }
    Add path to 3b-comments.js if needed: fetch(“comments-php/2-ajax-comments.php”,
    Global POST command should be filtered: Example: if ($_POST[“req”]) { switch ($_POST[“req”]) { should be: if (filter_input(INPUT_POST, ‘req’)) { switch (filter_input(INPUT_POST, ‘req’)) {
    HOWEVER, some updates caused the comment system to fail. Maybe you could figure this one out for the three lines of code affected in 2-ajax-comments.php?
    Otherwise, works great! Thanks.

    (B) Where do you place the response for moderation? “Your comment is awaiting moderation.” works great here.
    My page takes the comment, then resets the window without saying anything. Thanks.

    (C) I’m getting to where I really like this simple comment system. But I’m with Pankaj. I really need at least one level reply to make this comment system almost perfect.

    We can all see it works here…how about some code snippets and a few pointers to let us really try to do it?

    And where is your donate button? ;->

    1. (A1) Both UTF8 and UFT8MB4 work. One supports 3 bytes characters, the other supports 4 bytes. Initially used UTF8(MB3) for backward compatibility, will update the tutorial in due time as the world has moved forward to support mb4.
      (A2) No point validating the request. If it is invalid, the AJAX handler simply returns an “invalid request”.
      (B) As above, “MANUAL APPROVAL”.
      (C) As mentioned, it involves recursion and is not simple.

      Good luck with your project.
      https://code-boxx.com/faq/#help “Requests for new features may be considered, but will not be immediately answered.”

  3. Hello, I’m really get help by your amazing tutorial, but I got confused about how to delete the comment by the user. I already try to get the comment_id column and make changes in the comment.js but the $r[‘comment_id]; always filled with the oldest comment id that posted by the user. When I echo that it generates the exact comment_id but when it brings to the js file and back to the ajax-comment.php its value is back to the oldest comment id that posted by the user. I’m sorry if my explanation is confusing but I hope you understand and can give me some advice. Thanks in advance!

  4. Hi, thanks for this very useful source.
    But I want to ask is how to reverse data from mysql.
    That means I want the latest comment to appear first.
    Thank you very much!!!

    1. SORT COMMENTS IN ASCENDING OR DESCENDING ORDER
      2-ajax-comments.php
      // (C) SHOW COMMENTS
      ORDER BY `timestamp` ASC
      ORDER BY `timestamp` DESC

Leave a Comment

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